blob: aa19de0eced36e46223ca2562f831dc8d3e82070 [file] [log] [blame]
Hariharasubramanian Rcd387972016-01-20 07:19:41 -06001#!/usr/bin/env python
2
Hariharasubramanian R308cffc2016-03-03 09:35:16 -06003from subprocess import call, Popen, PIPE
Edward A. James75757c02016-07-15 09:11:18 -05004from IPy import IP
Hariharasubramanian Rcd387972016-01-20 07:19:41 -06005import sys
6import subprocess
7import dbus
8import string
Hariharasubramanian R308cffc2016-03-03 09:35:16 -06009import socket
10import re
Hariharasubramanian Rcd387972016-01-20 07:19:41 -060011import os
12import fcntl
13import glib
14import gobject
15import dbus.service
16import dbus.mainloop.glib
Vishwanatha Subbanna92a5d1e2016-08-30 21:23:32 +053017from ConfigParser import SafeConfigParser
18import glob
Hariharasubramanian Rcd387972016-01-20 07:19:41 -060019
Ratan Gupta8613b872016-10-07 17:15:58 +053020#MAC address mask for locally administered.
21MAC_LOCAL_ADMIN_MASK = 0x20000000000
22BROADCAST_MAC = 0xFFFFFFFFFFFF
Hariharasubramanian Rcd387972016-01-20 07:19:41 -060023DBUS_NAME = 'org.openbmc.NetworkManager'
24OBJ_NAME = '/org/openbmc/NetworkManager/Interface'
25
26network_providers = {
Adriana Kobylak88c733b2016-02-03 16:46:58 -060027 'networkd' : {
28 'bus_name' : 'org.freedesktop.network1',
29 'ip_object_name' : '/org/freedesktop/network1/network/default',
30 'hw_object_name' : '/org/freedesktop/network1/link/_31',
Hariharasubramanian R3a224e72016-02-10 12:32:08 -060031 'ip_if_name' : 'org.freedesktop.network1.Network',
32 'hw_if_name' : 'org.freedesktop.network1.Link',
Adriana Kobylak88c733b2016-02-03 16:46:58 -060033 'method' : 'org.freedesktop.network1.Network.SetAddr'
34 },
35 'NetworkManager' : {
36 'bus_name' : 'org.freedesktop.NetworkManager',
37 'ip_object_name' : '/org/freedesktop/NetworkManager',
38 'hw_object_name' : '/org/freedesktop/NetworkManager',
Hariharasubramanian R3a224e72016-02-10 12:32:08 -060039 'ip_if_name' : 'org.freedesktop.NetworkManager',
40 'hw_if_name' : 'org.freedesktop.NetworkManager',
Adriana Kobylak88c733b2016-02-03 16:46:58 -060041 'method' : 'org.freedesktop.NetworkManager' # FIXME:
42 },
Hariharasubramanian Rcd387972016-01-20 07:19:41 -060043}
44
45def getPrefixLen(mask):
Adriana Kobylak88c733b2016-02-03 16:46:58 -060046 prefixLen = sum([bin(int(x)).count('1') for x in mask.split('.')])
47 return prefixLen
Hariharasubramanian Rcd387972016-01-20 07:19:41 -060048
Vishwanatha Subbanna92a5d1e2016-08-30 21:23:32 +053049# Enable / Disable the UseDHCP setting in .network file
50def modifyNetConfig(confFile, usentp):
51 parser = SafeConfigParser()
52 parser.optionxform = str
53 parser.read(confFile)
54 sections = parser.sections()
55
56 if "Match" not in sections:
57 raise NameError, "[Match] section not found"
58
59 interface = parser.get('Match', 'Name')
60 if interface == '':
61 raise NameError, "Invalid interface"
62
63 if "DHCP" not in sections:
64 parser.add_section("DHCP")
65 if usentp.lower() == "yes":
66 parser.set('DHCP', 'UseNTP', "true")
67 elif usentp.lower() == "no":
68 parser.set('DHCP', 'UseNTP', "false")
69
70 print "Updating" + confFile + '\n'
71 with open(confFile, 'wb') as configfile:
72 parser.write(configfile)
73
74 rc = call(["ip", "addr", "flush", interface])
75 rc = call(["systemctl", "restart", "systemd-networkd.service"])
76 rc = call(["systemctl", "try-restart", "systemd-timesyncd.service"])
77 return rc
78
Ratan Gupta8613b872016-10-07 17:15:58 +053079
80def read_mac_provider_info():
81 conf_file = os.path.join('/etc', 'network-manager.conf')
82 if not os.path.exists(conf_file):
83 raise IOError("Could not find %s" % conf_file)
84
85 parser = SafeConfigParser()
86 parser.optionxform = str
87 parser.read(conf_file)
88 sections = parser.sections()
89
90 path = parser.get('mac_loc', 'path')
91 if not path:
92 raise ValueError("Empty path")
93
94 bus_name = parser.get('mac_loc', 'bus')
95 if not bus_name:
96 raise ValueError("Empty bus-name")
97
98 intf = parser.get('mac_loc', 'interface')
99 if not intf:
100 raise ValueError("Empty intf-name")
101
102 prop = parser.get('mac_loc', 'property')
103 if not prop:
104 raise ValueError("Empty prop-name")
105
106 return bus_name, path, intf, prop
107
108
109# Get Mac address from the eeprom
110def get_mac_from_eeprom():
111 bus = dbus.SystemBus()
112 bus_name, path, intf, prop = read_mac_provider_info()
113 obj = bus.get_object(bus_name, path)
114 dbus_method = obj.get_dbus_method("Get", dbus.PROPERTIES_IFACE)
115 return dbus_method(intf, prop)
116
117
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600118class IfAddr ():
119 def __init__ (self, family, scope, flags, prefixlen, addr, gw):
120 self.family = family
121 self.scope = scope
122 self.flags = flags
123 self.prefixlen = prefixlen
124 self.addr = addr
125 self.gw = gw
126
127class NetMan (dbus.service.Object):
128 def __init__(self, bus, name):
129 self.bus = bus
130 self.name = name
131 dbus.service.Object.__init__(self,bus,name)
132
133 def setNetworkProvider(self, provider):
134 self.provider = provider
135
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600136 def _isvaliddev(self, device):
137 devices = os.listdir ("/sys/class/net")
138 if not device in devices : return False
139 else: return True
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600140
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600141 def _ishwdev (self, device):
142 f = open ("/sys/class/net/"+device+"/type")
143 type = f.read()
144 return False if (int(type) == 772) else True
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600145
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600146 def _isvalidmask (self, mask):
147 for x in mask.split('.'):
148 try:
149 y = int(x)
150 except:
151 return False
152 if y > 255: return False
153 return mask.count('.') == 3
154
Ratan Gupta8613b872016-10-07 17:15:58 +0530155 def validatemac(self, mac):
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600156 macre = '([a-fA-F0-9]{2}[:|\-]?){6}'
Ratan Gupta8613b872016-10-07 17:15:58 +0530157 if re.compile(macre).search(mac) is None:
158 raise ValueError("Malformed MAC address")
159
160 # Don't allow Broadcast or global unique mac
161 int_mac = int(mac.replace(":", ""), 16)
162 if not (int_mac ^ BROADCAST_MAC):
163 raise ValueError("Given Mac is BroadCast Mac Address")
164
165 if not int_mac & MAC_LOCAL_ADMIN_MASK:
166 int_eep_mac = int(get_mac_from_eeprom(), 16)
167 if int_eep_mac != int_mac:
168 raise ValueError("Given MAC address is neither a local Admin type \
169 nor is same as in eeprom")
170
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600171
Edward A. James75757c02016-07-15 09:11:18 -0500172 def _isvalidipv4(self, ipstr, netmask):
173 ip_parts = ipstr.split(".")
174 if len(ip_parts) != 4:
175 return "Malformed"
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600176
Edward A. James75757c02016-07-15 09:11:18 -0500177 first, second, third, fourth = [int(part) for part in ip_parts]
178 if first == 0 and second == 0 and third == 0 and fourth == 0:
179 return "Invalid" # "this" network disallowed
180 if first == 169 and second == 254:
181 return "Link Local"
182 if first >= 224:
183 return "Invalid" # class D multicast and class E disallowed
184 if first == 192 and second == 88 and third == 99:
185 return "Invalid" # ipv6 relay
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600186
Edward A. James75757c02016-07-15 09:11:18 -0500187 # check validity against netmask
188 if netmask != '0':
189 ip_bin = (first << 24) + (second << 16) + (third << 8) + fourth
190 mask_parts = netmask.split(".")
191 if len(mask_parts) == 4: # long form netmask
192 mask_bin = (int(mask_parts[0]) << 24) + (int(mask_parts[1]) << 16) + (int(mask_parts[2]) << 8) + int(mask_parts[3])
193 elif netmask.count(".") == 0: # short form netmask
194 mask_bin = 0xffffffff ^ (1 << 32 - int(netmask)) - 1
195 else:
196 return "Malformed" # bad netmask
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600197
Edward A. James75757c02016-07-15 09:11:18 -0500198 if ip_bin & ~mask_bin == 0:
199 return "Invalid" # disallowed by this netmask
200 if ip_bin | mask_bin == 0xFFFFFFFF:
201 return "Invalid" # disallowed by this netmask
202
203 return "Valid"
204
205
206 def _isvalidip(self, ipaddr, netmask = '0'):
207 try:
208 ip = IP(ipaddr)
209 except ValueError:
210 return "Malformed"
211
212 ipstr = ip.strNormal(0)
213 ipstr_masked = ip.strNormal(2)
214 if ipstr_masked.count("/") != 0 and netmask == '0':
215 netmask = ipstr_masked.split("/")[1]
216
217 if ip.version() == 4: # additional checks for ipv4
218 return self._isvalidipv4(ipstr, netmask)
219 # TODO: check ipv6 openbmc/openbmc#496
220
221 return "Valid"
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600222
223 def _getAddr (self, target, device):
224 netprov = network_providers [self.provider]
225 bus_name = netprov ['bus_name']
226
227 if (target == "ip"):
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600228 ipaddr = ""
229 defgw = ""
230 prefixlen = "0"
231
232 proc = subprocess.Popen(["ip", "addr", "show", "dev", device], stdout=PIPE)
233 procout = proc.communicate()
234 if procout:
235 ipout = procout[0].splitlines()[2].strip()
236 ipaddr,prefixlen = ipout.split ()[1].split("/")
237
238 proc = subprocess.Popen(["ip", "route", "show", "dev", device, "default", "0.0.0.0/0"], stdout=PIPE)
239 procout = proc.communicate()
240 if procout[0]:
241 ipout = procout[0].splitlines()[0].strip()
242 defgw = ipout.split ()[2]
243
244 return 2, int(prefixlen), ipaddr, defgw
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600245
246 if (target == "mac"):
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600247 proc = subprocess.Popen(["ip", "link", "show", "dev", device], stdout=PIPE)
248 ipout = proc.communicate()[0].splitlines()[1].strip()
249 mac = ipout.split ()[1]
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600250 return mac
251
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600252 @dbus.service.method(DBUS_NAME, "", "")
253 def test(self):
254 print("TEST")
255
Vishwanatha Subbanna92a5d1e2016-08-30 21:23:32 +0530256 @dbus.service.method(DBUS_NAME, "sas", "x")
257 def SetNtpServer (self, device, ntpservers):
258 if not self._isvaliddev (device) : raise ValueError, "Invalid Device"
259
260 # Convert the array into space separated value string
261 ntp_ip = " ".join(ntpservers)
262 if not ntp_ip : raise ValueError, "Invalid Data"
263
264 confFile = "/etc/systemd/network/00-bmc-" + device + ".network"
265
266 parser = SafeConfigParser()
267 parser.optionxform = str
268 parser.read(confFile)
269 sections = parser.sections()
270 if "Match" not in sections:
271 raise NameError, "[Match] section not found"
272
273 interface = parser.get('Match', 'Name')
274 if interface != device:
275 raise ValueError, "Device [" + device + "] Not Configured"
276
277 if "Network" not in sections:
278 raise NameError, "[Network] section not found"
279
280 parser.set('Network', 'NTP', ntp_ip)
281 print "Updating " + confFile + '\n'
282 with open(confFile, 'wb') as configfile:
283 parser.write(configfile)
284 rc = call(["ip", "addr", "flush", device])
285 rc = call(["systemctl", "restart", "systemd-networkd.service"])
286 rc = call(["systemctl", "try-restart", "systemd-timesyncd.service"])
287 return rc
288
289 @dbus.service.method(DBUS_NAME, "s", "x")
290 def UpdateUseNtpField (self, usentp):
291 filelist = glob.glob("/etc/systemd/network/*.network")
292 for configfile in filelist:
293 modifyNetConfig(configfile,usentp)
294 return 0
295
Hariharasubramanian R3a224e72016-02-10 12:32:08 -0600296 @dbus.service.method(DBUS_NAME, "s", "x")
297 def EnableDHCP (self, device):
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600298 if not self._isvaliddev (device) : raise ValueError, "Invalid Device"
299
300 confFile = "/etc/systemd/network/00-bmc-" + device + ".network"
Hariharasubramanian R3a224e72016-02-10 12:32:08 -0600301
302 print("Making .network file...")
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600303 try:
304 networkconf = open (confFile, "w+")
305 except IOError:
306 raise IOError, "Failed to open " + confFile
307
Hariharasubramanian R3a224e72016-02-10 12:32:08 -0600308 networkconf.write ('[Match]'+ '\n')
309 networkconf.write ('Name=' + (device) + '\n')
310 networkconf.write ('[Network]' + '\n')
Ratan Gupta968d2032017-03-24 19:59:44 +0530311 networkconf.write ('DHCP=yes' + '\n')
Patrick Williamsec01f6e2017-03-08 22:00:23 -0600312 networkconf.write ('[DHCP]' + '\n')
313 networkconf.write ('ClientIdentifier=mac' + '\n')
Hariharasubramanian R3a224e72016-02-10 12:32:08 -0600314 networkconf.close ()
315
316 print("Restarting networkd service...")
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600317 rc = call(["ip", "addr", "flush", device])
318 rc = call(["systemctl", "restart", "systemd-networkd.service"])
319 return rc
Hariharasubramanian R3a224e72016-02-10 12:32:08 -0600320
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600321 @dbus.service.method(DBUS_NAME, "ssss", "x")
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600322 def SetAddress4 (self, device, ipaddr, netmask, gateway):
323 if not self._isvaliddev (device) : raise ValueError, "Invalid Device"
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600324 if not self._isvalidmask (netmask) : raise ValueError, "Invalid Mask"
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600325 prefixLen = getPrefixLen (netmask)
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600326 if prefixLen == 0: raise ValueError, "Invalid Mask"
Edward A. James75757c02016-07-15 09:11:18 -0500327 valid = self._isvalidip (ipaddr, netmask)
328 if valid != "Valid": raise ValueError, valid + " IP Address"
329 valid = self._isvalidip (gateway)
330 if valid != "Valid": raise ValueError, valid + " IP Address"
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600331
332 confFile = "/etc/systemd/network/00-bmc-" + device + ".network"
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600333
334 print("Making .network file...")
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600335 try:
336 networkconf = open (confFile, "w+")
337 except IOError:
338 raise IOError, "Failed to open " + confFile
339
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600340 networkconf.write ('[Match]'+ '\n')
341 networkconf.write ('Name=' + (device) + '\n')
342 networkconf.write ('[Network]' + '\n')
343 networkconf.write ('Address=' + ipaddr + '/' + str(prefixLen) + '\n')
344 networkconf.write ('Gateway=' + gateway + '\n')
Chris Austenb13a3cd2016-02-01 18:18:21 -0600345 networkconf.close()
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600346
347 print("Restarting networkd service...")
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600348 rc = call(["ip", "addr", "flush", device])
349 rc = call(["systemctl", "restart", "systemd-networkd.service"])
350 return rc
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600351
Hariharasubramanian Rcc28a6f2016-04-29 12:25:12 -0500352 @dbus.service.method(DBUS_NAME, "s", "s")
353 def GetAddressType (self, device):
354 if not self._isvaliddev (device) : raise ValueError, "Invalid Device"
355
356 confFile = "/etc/systemd/network/00-bmc-" + device + ".network"
357 if not os.path.exists(confFile):
358 print "Config file (%s) not found !" % confFile
359 netprov = network_providers [self.provider]
360 bus_name = netprov ['bus_name']
361 obj_name = netprov ['ip_object_name']
362 o = self.bus.get_object(bus_name, obj_name, introspect=False)
363 i = dbus.Interface(o, 'org.freedesktop.DBus.Properties')
364 f = i.Get (netprov ['ip_if_name'], "SourcePath")
365 print "Using default networkd config file (%s)" % f
366 confFile = f
367
368 with open(confFile, "r") as f:
369 for line in f:
370 config = line.split ("=")
371 if (len (config) < 2) : continue
372 if config [0].upper() == "DHCP":
373 v = config[1].strip().upper()
374 if (v=="YES" or v=="IPV4" or v=="IPV6"):
375 return "DHCP"
376 return "STATIC"
377
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600378 #family, prefixlen, ip, defgw
379 @dbus.service.method(DBUS_NAME, "s", "iyss")
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600380 def GetAddress4 (self, device):
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600381 if not self._isvaliddev (device) : raise ValueError, "Invalid Device"
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600382 return self._getAddr ("ip", device)
383
384 @dbus.service.method(DBUS_NAME, "s", "s")
385 def GetHwAddress (self, device):
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600386 if not self._isvaliddev (device) : raise ValueError, "Invalid Device"
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600387 return self._getAddr ("mac", device)
388
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600389 @dbus.service.method(DBUS_NAME, "ss", "i")
390 def SetHwAddress (self, device, mac):
391 if not self._isvaliddev (device) : raise ValueError, "Invalid Device"
392 if not self._ishwdev (device) : raise ValueError, "Not a Hardware Device"
Ratan Gupta8613b872016-10-07 17:15:58 +0530393
394 self.validatemac(mac)
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600395
Adriana Kobylak88c733b2016-02-03 16:46:58 -0600396 rc = subprocess.call(["fw_setenv", "ethaddr", mac])
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600397
398 print("Restarting networkd service...")
399 rc = call(["ip", "link", "set", "dev", device, "down"])
400 rc = call(["ip", "link", "set", "dev", device, "address", mac])
401 rc = call(["ip", "link", "set", "dev", device, "up"])
402
403 rc = call(["systemctl", "restart", "systemd-networkd.service"])
Adriana Kobylak88c733b2016-02-03 16:46:58 -0600404 return rc
405
vishwa5c912c82016-03-24 07:59:28 -0500406 #string of nameservers
407 @dbus.service.method(DBUS_NAME,"s", "s")
408 def SetNameServers (self, nameservers):
409 dns_entry = nameservers.split()
410 fail_msg = ''
411 dhcp_auto = False
412 file_opened = False
413 if len(dns_entry) > 0:
414 for dns in dns_entry:
Edward A. James75757c02016-07-15 09:11:18 -0500415 valid = self._isvalidip (dns)
416 if valid != "Valid":
vishwa5c912c82016-03-24 07:59:28 -0500417 if dns == "DHCP_AUTO=":
418 #This DNS is supplied by DHCP.
419 dhcp_auto = True
420 else:
Edward A. James75757c02016-07-15 09:11:18 -0500421 print valid + " DNS Address [" + dns + "]"
vishwa5c912c82016-03-24 07:59:28 -0500422 fail_msg = fail_msg + '[' + dns + ']'
423 else:
424 #Only over write on a first valid input
425 if file_opened == False:
426 resolv_conf = open("/etc/resolv.conf",'w')
427 file_opened = True
428 if dhcp_auto == True:
429 resolv_conf.write("### Generated automatically via DHCP ###\n")
430 else:
431 resolv_conf.write("### Generated manually via dbus settings ###\n")
432 dns_ip = 'nameserver ' + dns + '\n'
433 resolv_conf.write(dns_ip)
434 if file_opened == True:
435 resolv_conf.close()
436 else:
437 raise ValueError, "Invalid DNS entry"
438 if len(fail_msg) > 0:
439 return 'Failures encountered processing' + fail_msg
440 else:
441 return "DNS entries updated Successfully"
442
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600443def main():
444 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
445 bus = dbus.SystemBus()
446 name = dbus.service.BusName(DBUS_NAME, bus)
447 obj = NetMan (bus, OBJ_NAME)
448 obj.setNetworkProvider ("networkd")
449 mainloop = gobject.MainLoop()
450 print("Started")
451 mainloop.run()
452
453if __name__ == '__main__':
Adriana Kobylake150bfd2016-02-01 21:47:34 -0600454 sys.exit(main())