blob: 99984d0d69b3cb360fefcffd968dd440e135c19b [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')
311 networkconf.write ('DHCP=yes')
312 networkconf.close ()
313
314 print("Restarting networkd service...")
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600315 rc = call(["ip", "addr", "flush", device])
316 rc = call(["systemctl", "restart", "systemd-networkd.service"])
317 return rc
Hariharasubramanian R3a224e72016-02-10 12:32:08 -0600318
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600319 @dbus.service.method(DBUS_NAME, "ssss", "x")
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600320 def SetAddress4 (self, device, ipaddr, netmask, gateway):
321 if not self._isvaliddev (device) : raise ValueError, "Invalid Device"
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600322 if not self._isvalidmask (netmask) : raise ValueError, "Invalid Mask"
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600323 prefixLen = getPrefixLen (netmask)
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600324 if prefixLen == 0: raise ValueError, "Invalid Mask"
Edward A. James75757c02016-07-15 09:11:18 -0500325 valid = self._isvalidip (ipaddr, netmask)
326 if valid != "Valid": raise ValueError, valid + " IP Address"
327 valid = self._isvalidip (gateway)
328 if valid != "Valid": raise ValueError, valid + " IP Address"
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600329
330 confFile = "/etc/systemd/network/00-bmc-" + device + ".network"
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600331
332 print("Making .network file...")
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600333 try:
334 networkconf = open (confFile, "w+")
335 except IOError:
336 raise IOError, "Failed to open " + confFile
337
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600338 networkconf.write ('[Match]'+ '\n')
339 networkconf.write ('Name=' + (device) + '\n')
340 networkconf.write ('[Network]' + '\n')
341 networkconf.write ('Address=' + ipaddr + '/' + str(prefixLen) + '\n')
342 networkconf.write ('Gateway=' + gateway + '\n')
Chris Austenb13a3cd2016-02-01 18:18:21 -0600343 networkconf.close()
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600344
345 print("Restarting networkd service...")
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600346 rc = call(["ip", "addr", "flush", device])
347 rc = call(["systemctl", "restart", "systemd-networkd.service"])
348 return rc
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600349
Hariharasubramanian Rcc28a6f2016-04-29 12:25:12 -0500350 @dbus.service.method(DBUS_NAME, "s", "s")
351 def GetAddressType (self, device):
352 if not self._isvaliddev (device) : raise ValueError, "Invalid Device"
353
354 confFile = "/etc/systemd/network/00-bmc-" + device + ".network"
355 if not os.path.exists(confFile):
356 print "Config file (%s) not found !" % confFile
357 netprov = network_providers [self.provider]
358 bus_name = netprov ['bus_name']
359 obj_name = netprov ['ip_object_name']
360 o = self.bus.get_object(bus_name, obj_name, introspect=False)
361 i = dbus.Interface(o, 'org.freedesktop.DBus.Properties')
362 f = i.Get (netprov ['ip_if_name'], "SourcePath")
363 print "Using default networkd config file (%s)" % f
364 confFile = f
365
366 with open(confFile, "r") as f:
367 for line in f:
368 config = line.split ("=")
369 if (len (config) < 2) : continue
370 if config [0].upper() == "DHCP":
371 v = config[1].strip().upper()
372 if (v=="YES" or v=="IPV4" or v=="IPV6"):
373 return "DHCP"
374 return "STATIC"
375
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600376 #family, prefixlen, ip, defgw
377 @dbus.service.method(DBUS_NAME, "s", "iyss")
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600378 def GetAddress4 (self, device):
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600379 if not self._isvaliddev (device) : raise ValueError, "Invalid Device"
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600380 return self._getAddr ("ip", device)
381
382 @dbus.service.method(DBUS_NAME, "s", "s")
383 def GetHwAddress (self, device):
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600384 if not self._isvaliddev (device) : raise ValueError, "Invalid Device"
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600385 return self._getAddr ("mac", device)
386
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600387 @dbus.service.method(DBUS_NAME, "ss", "i")
388 def SetHwAddress (self, device, mac):
389 if not self._isvaliddev (device) : raise ValueError, "Invalid Device"
390 if not self._ishwdev (device) : raise ValueError, "Not a Hardware Device"
Ratan Gupta8613b872016-10-07 17:15:58 +0530391
392 self.validatemac(mac)
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600393
Adriana Kobylak88c733b2016-02-03 16:46:58 -0600394 rc = subprocess.call(["fw_setenv", "ethaddr", mac])
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600395
396 print("Restarting networkd service...")
397 rc = call(["ip", "link", "set", "dev", device, "down"])
398 rc = call(["ip", "link", "set", "dev", device, "address", mac])
399 rc = call(["ip", "link", "set", "dev", device, "up"])
400
401 rc = call(["systemctl", "restart", "systemd-networkd.service"])
Adriana Kobylak88c733b2016-02-03 16:46:58 -0600402 return rc
403
vishwa5c912c82016-03-24 07:59:28 -0500404 #string of nameservers
405 @dbus.service.method(DBUS_NAME,"s", "s")
406 def SetNameServers (self, nameservers):
407 dns_entry = nameservers.split()
408 fail_msg = ''
409 dhcp_auto = False
410 file_opened = False
411 if len(dns_entry) > 0:
412 for dns in dns_entry:
Edward A. James75757c02016-07-15 09:11:18 -0500413 valid = self._isvalidip (dns)
414 if valid != "Valid":
vishwa5c912c82016-03-24 07:59:28 -0500415 if dns == "DHCP_AUTO=":
416 #This DNS is supplied by DHCP.
417 dhcp_auto = True
418 else:
Edward A. James75757c02016-07-15 09:11:18 -0500419 print valid + " DNS Address [" + dns + "]"
vishwa5c912c82016-03-24 07:59:28 -0500420 fail_msg = fail_msg + '[' + dns + ']'
421 else:
422 #Only over write on a first valid input
423 if file_opened == False:
424 resolv_conf = open("/etc/resolv.conf",'w')
425 file_opened = True
426 if dhcp_auto == True:
427 resolv_conf.write("### Generated automatically via DHCP ###\n")
428 else:
429 resolv_conf.write("### Generated manually via dbus settings ###\n")
430 dns_ip = 'nameserver ' + dns + '\n'
431 resolv_conf.write(dns_ip)
432 if file_opened == True:
433 resolv_conf.close()
434 else:
435 raise ValueError, "Invalid DNS entry"
436 if len(fail_msg) > 0:
437 return 'Failures encountered processing' + fail_msg
438 else:
439 return "DNS entries updated Successfully"
440
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600441def main():
442 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
443 bus = dbus.SystemBus()
444 name = dbus.service.BusName(DBUS_NAME, bus)
445 obj = NetMan (bus, OBJ_NAME)
446 obj.setNetworkProvider ("networkd")
447 mainloop = gobject.MainLoop()
448 print("Started")
449 mainloop.run()
450
451if __name__ == '__main__':
Adriana Kobylake150bfd2016-02-01 21:47:34 -0600452 sys.exit(main())