blob: a99e5cc6a6198e06e56e2aec60df8a3d06c08581 [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
Dinesh Chinarie8d42112017-06-18 23:28:32 -050019import obmc.mapper
Hariharasubramanian Rcd387972016-01-20 07:19:41 -060020
Ratan Gupta8613b872016-10-07 17:15:58 +053021#MAC address mask for locally administered.
22MAC_LOCAL_ADMIN_MASK = 0x20000000000
23BROADCAST_MAC = 0xFFFFFFFFFFFF
Hariharasubramanian Rcd387972016-01-20 07:19:41 -060024DBUS_NAME = 'org.openbmc.NetworkManager'
25OBJ_NAME = '/org/openbmc/NetworkManager/Interface'
Dinesh Chinarie8d42112017-06-18 23:28:32 -050026INV_INTF_NAME = 'xyz.openbmc_project.Inventory.Item.NetworkInterface'
27INVENTORY_ROOT = '/xyz/openbmc_project/inventory'
28MAC_PROPERTY = 'MACAddress'
Hariharasubramanian Rcd387972016-01-20 07:19:41 -060029
30network_providers = {
Dinesh Chinarie8d42112017-06-18 23:28:32 -050031 'networkd' : {
Adriana Kobylak88c733b2016-02-03 16:46:58 -060032 'bus_name' : 'org.freedesktop.network1',
33 'ip_object_name' : '/org/freedesktop/network1/network/default',
34 'hw_object_name' : '/org/freedesktop/network1/link/_31',
Hariharasubramanian R3a224e72016-02-10 12:32:08 -060035 'ip_if_name' : 'org.freedesktop.network1.Network',
36 'hw_if_name' : 'org.freedesktop.network1.Link',
Adriana Kobylak88c733b2016-02-03 16:46:58 -060037 'method' : 'org.freedesktop.network1.Network.SetAddr'
38 },
39 'NetworkManager' : {
40 'bus_name' : 'org.freedesktop.NetworkManager',
41 'ip_object_name' : '/org/freedesktop/NetworkManager',
42 'hw_object_name' : '/org/freedesktop/NetworkManager',
Hariharasubramanian R3a224e72016-02-10 12:32:08 -060043 'ip_if_name' : 'org.freedesktop.NetworkManager',
44 'hw_if_name' : 'org.freedesktop.NetworkManager',
Adriana Kobylak88c733b2016-02-03 16:46:58 -060045 'method' : 'org.freedesktop.NetworkManager' # FIXME:
46 },
Hariharasubramanian Rcd387972016-01-20 07:19:41 -060047}
48
49def getPrefixLen(mask):
Adriana Kobylak88c733b2016-02-03 16:46:58 -060050 prefixLen = sum([bin(int(x)).count('1') for x in mask.split('.')])
51 return prefixLen
Hariharasubramanian Rcd387972016-01-20 07:19:41 -060052
Vishwanatha Subbanna92a5d1e2016-08-30 21:23:32 +053053# Enable / Disable the UseDHCP setting in .network file
54def modifyNetConfig(confFile, usentp):
55 parser = SafeConfigParser()
56 parser.optionxform = str
57 parser.read(confFile)
58 sections = parser.sections()
59
60 if "Match" not in sections:
61 raise NameError, "[Match] section not found"
62
63 interface = parser.get('Match', 'Name')
64 if interface == '':
65 raise NameError, "Invalid interface"
66
67 if "DHCP" not in sections:
68 parser.add_section("DHCP")
69 if usentp.lower() == "yes":
70 parser.set('DHCP', 'UseNTP', "true")
71 elif usentp.lower() == "no":
72 parser.set('DHCP', 'UseNTP', "false")
73
74 print "Updating" + confFile + '\n'
75 with open(confFile, 'wb') as configfile:
76 parser.write(configfile)
77
78 rc = call(["ip", "addr", "flush", interface])
79 rc = call(["systemctl", "restart", "systemd-networkd.service"])
80 rc = call(["systemctl", "try-restart", "systemd-timesyncd.service"])
81 return rc
82
Ratan Gupta8613b872016-10-07 17:15:58 +053083
Ratan Gupta8613b872016-10-07 17:15:58 +053084# Get Mac address from the eeprom
85def get_mac_from_eeprom():
86 bus = dbus.SystemBus()
Dinesh Chinarie8d42112017-06-18 23:28:32 -050087 mapper = obmc.mapper.Mapper(bus)
Ratan Gupta8613b872016-10-07 17:15:58 +053088
Dinesh Chinarie8d42112017-06-18 23:28:32 -050089 # Get the inventory subtree, limited
90 # to objects that implement NetworkInterface.
91 for path, info in \
92 mapper.get_subtree(
93 path=INVENTORY_ROOT,
94 interfaces=[INV_INTF_NAME]).iteritems():
95 # Find a NetworkInterface with 'bmc' in the path.
96 if 'bmc' not in path:
97 continue
98
99 # Only expecting a single service to implement
100 # NetworkInterface. Get the service connection
101 # from the mapper response
102 conn = info.keys()[0]
103
104 # Get the inventory object implementing NetworkInterface.
105 obj = bus.get_object(conn, path)
106
107 # Get the MAC address
108 mproxy = obj.get_dbus_method('Get', dbus.PROPERTIES_IFACE)
109 return mproxy(INV_INTF_NAME, MAC_PROPERTY)
Ratan Gupta8613b872016-10-07 17:15:58 +0530110
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600111class IfAddr ():
112 def __init__ (self, family, scope, flags, prefixlen, addr, gw):
113 self.family = family
114 self.scope = scope
115 self.flags = flags
116 self.prefixlen = prefixlen
117 self.addr = addr
118 self.gw = gw
119
120class NetMan (dbus.service.Object):
121 def __init__(self, bus, name):
122 self.bus = bus
123 self.name = name
124 dbus.service.Object.__init__(self,bus,name)
125
126 def setNetworkProvider(self, provider):
127 self.provider = provider
128
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600129 def _isvaliddev(self, device):
130 devices = os.listdir ("/sys/class/net")
131 if not device in devices : return False
132 else: return True
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600133
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600134 def _ishwdev (self, device):
135 f = open ("/sys/class/net/"+device+"/type")
136 type = f.read()
137 return False if (int(type) == 772) else True
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600138
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600139 def _isvalidmask (self, mask):
140 for x in mask.split('.'):
141 try:
142 y = int(x)
143 except:
144 return False
145 if y > 255: return False
146 return mask.count('.') == 3
147
Ratan Gupta8613b872016-10-07 17:15:58 +0530148 def validatemac(self, mac):
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600149 macre = '([a-fA-F0-9]{2}[:|\-]?){6}'
Ratan Gupta8613b872016-10-07 17:15:58 +0530150 if re.compile(macre).search(mac) is None:
151 raise ValueError("Malformed MAC address")
152
153 # Don't allow Broadcast or global unique mac
154 int_mac = int(mac.replace(":", ""), 16)
155 if not (int_mac ^ BROADCAST_MAC):
156 raise ValueError("Given Mac is BroadCast Mac Address")
157
158 if not int_mac & MAC_LOCAL_ADMIN_MASK:
Dinesh Chinarie8d42112017-06-18 23:28:32 -0500159 eep_mac = get_mac_from_eeprom()
160 if eep_mac:
161 int_eep_mac = int(eep_mac, 16)
162 if int_eep_mac != int_mac:
163 raise ValueError("Given MAC address is neither a local Admin type \
Ratan Gupta8613b872016-10-07 17:15:58 +0530164 nor is same as in eeprom")
165
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600166
Edward A. James75757c02016-07-15 09:11:18 -0500167 def _isvalidipv4(self, ipstr, netmask):
168 ip_parts = ipstr.split(".")
169 if len(ip_parts) != 4:
170 return "Malformed"
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600171
Edward A. James75757c02016-07-15 09:11:18 -0500172 first, second, third, fourth = [int(part) for part in ip_parts]
173 if first == 0 and second == 0 and third == 0 and fourth == 0:
174 return "Invalid" # "this" network disallowed
175 if first == 169 and second == 254:
176 return "Link Local"
177 if first >= 224:
178 return "Invalid" # class D multicast and class E disallowed
179 if first == 192 and second == 88 and third == 99:
180 return "Invalid" # ipv6 relay
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600181
Edward A. James75757c02016-07-15 09:11:18 -0500182 # check validity against netmask
183 if netmask != '0':
184 ip_bin = (first << 24) + (second << 16) + (third << 8) + fourth
185 mask_parts = netmask.split(".")
186 if len(mask_parts) == 4: # long form netmask
187 mask_bin = (int(mask_parts[0]) << 24) + (int(mask_parts[1]) << 16) + (int(mask_parts[2]) << 8) + int(mask_parts[3])
188 elif netmask.count(".") == 0: # short form netmask
189 mask_bin = 0xffffffff ^ (1 << 32 - int(netmask)) - 1
190 else:
191 return "Malformed" # bad netmask
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600192
Edward A. James75757c02016-07-15 09:11:18 -0500193 if ip_bin & ~mask_bin == 0:
194 return "Invalid" # disallowed by this netmask
195 if ip_bin | mask_bin == 0xFFFFFFFF:
196 return "Invalid" # disallowed by this netmask
197
198 return "Valid"
199
200
201 def _isvalidip(self, ipaddr, netmask = '0'):
202 try:
203 ip = IP(ipaddr)
204 except ValueError:
205 return "Malformed"
206
207 ipstr = ip.strNormal(0)
208 ipstr_masked = ip.strNormal(2)
209 if ipstr_masked.count("/") != 0 and netmask == '0':
210 netmask = ipstr_masked.split("/")[1]
211
212 if ip.version() == 4: # additional checks for ipv4
213 return self._isvalidipv4(ipstr, netmask)
214 # TODO: check ipv6 openbmc/openbmc#496
215
216 return "Valid"
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600217
218 def _getAddr (self, target, device):
219 netprov = network_providers [self.provider]
220 bus_name = netprov ['bus_name']
221
222 if (target == "ip"):
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600223 ipaddr = ""
224 defgw = ""
225 prefixlen = "0"
226
227 proc = subprocess.Popen(["ip", "addr", "show", "dev", device], stdout=PIPE)
228 procout = proc.communicate()
229 if procout:
230 ipout = procout[0].splitlines()[2].strip()
231 ipaddr,prefixlen = ipout.split ()[1].split("/")
232
233 proc = subprocess.Popen(["ip", "route", "show", "dev", device, "default", "0.0.0.0/0"], stdout=PIPE)
234 procout = proc.communicate()
235 if procout[0]:
236 ipout = procout[0].splitlines()[0].strip()
237 defgw = ipout.split ()[2]
238
239 return 2, int(prefixlen), ipaddr, defgw
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600240
241 if (target == "mac"):
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600242 proc = subprocess.Popen(["ip", "link", "show", "dev", device], stdout=PIPE)
243 ipout = proc.communicate()[0].splitlines()[1].strip()
244 mac = ipout.split ()[1]
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600245 return mac
246
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600247 @dbus.service.method(DBUS_NAME, "", "")
248 def test(self):
249 print("TEST")
250
Vishwanatha Subbanna92a5d1e2016-08-30 21:23:32 +0530251 @dbus.service.method(DBUS_NAME, "sas", "x")
252 def SetNtpServer (self, device, ntpservers):
253 if not self._isvaliddev (device) : raise ValueError, "Invalid Device"
254
255 # Convert the array into space separated value string
256 ntp_ip = " ".join(ntpservers)
257 if not ntp_ip : raise ValueError, "Invalid Data"
258
259 confFile = "/etc/systemd/network/00-bmc-" + device + ".network"
260
261 parser = SafeConfigParser()
262 parser.optionxform = str
263 parser.read(confFile)
264 sections = parser.sections()
265 if "Match" not in sections:
266 raise NameError, "[Match] section not found"
267
268 interface = parser.get('Match', 'Name')
269 if interface != device:
270 raise ValueError, "Device [" + device + "] Not Configured"
271
272 if "Network" not in sections:
273 raise NameError, "[Network] section not found"
274
275 parser.set('Network', 'NTP', ntp_ip)
276 print "Updating " + confFile + '\n'
277 with open(confFile, 'wb') as configfile:
278 parser.write(configfile)
279 rc = call(["ip", "addr", "flush", device])
280 rc = call(["systemctl", "restart", "systemd-networkd.service"])
281 rc = call(["systemctl", "try-restart", "systemd-timesyncd.service"])
282 return rc
283
284 @dbus.service.method(DBUS_NAME, "s", "x")
285 def UpdateUseNtpField (self, usentp):
286 filelist = glob.glob("/etc/systemd/network/*.network")
287 for configfile in filelist:
288 modifyNetConfig(configfile,usentp)
289 return 0
290
Hariharasubramanian R3a224e72016-02-10 12:32:08 -0600291 @dbus.service.method(DBUS_NAME, "s", "x")
292 def EnableDHCP (self, device):
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600293 if not self._isvaliddev (device) : raise ValueError, "Invalid Device"
294
295 confFile = "/etc/systemd/network/00-bmc-" + device + ".network"
Hariharasubramanian R3a224e72016-02-10 12:32:08 -0600296
297 print("Making .network file...")
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600298 try:
299 networkconf = open (confFile, "w+")
300 except IOError:
301 raise IOError, "Failed to open " + confFile
302
Hariharasubramanian R3a224e72016-02-10 12:32:08 -0600303 networkconf.write ('[Match]'+ '\n')
304 networkconf.write ('Name=' + (device) + '\n')
305 networkconf.write ('[Network]' + '\n')
Ratan Gupta968d2032017-03-24 19:59:44 +0530306 networkconf.write ('DHCP=yes' + '\n')
Patrick Williamsec01f6e2017-03-08 22:00:23 -0600307 networkconf.write ('[DHCP]' + '\n')
308 networkconf.write ('ClientIdentifier=mac' + '\n')
Hariharasubramanian R3a224e72016-02-10 12:32:08 -0600309 networkconf.close ()
310
311 print("Restarting networkd service...")
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600312 rc = call(["ip", "addr", "flush", device])
313 rc = call(["systemctl", "restart", "systemd-networkd.service"])
314 return rc
Hariharasubramanian R3a224e72016-02-10 12:32:08 -0600315
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600316 @dbus.service.method(DBUS_NAME, "ssss", "x")
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600317 def SetAddress4 (self, device, ipaddr, netmask, gateway):
318 if not self._isvaliddev (device) : raise ValueError, "Invalid Device"
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600319 if not self._isvalidmask (netmask) : raise ValueError, "Invalid Mask"
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600320 prefixLen = getPrefixLen (netmask)
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600321 if prefixLen == 0: raise ValueError, "Invalid Mask"
Edward A. James75757c02016-07-15 09:11:18 -0500322 valid = self._isvalidip (ipaddr, netmask)
323 if valid != "Valid": raise ValueError, valid + " IP Address"
324 valid = self._isvalidip (gateway)
325 if valid != "Valid": raise ValueError, valid + " IP Address"
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600326
327 confFile = "/etc/systemd/network/00-bmc-" + device + ".network"
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600328
329 print("Making .network file...")
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600330 try:
331 networkconf = open (confFile, "w+")
332 except IOError:
333 raise IOError, "Failed to open " + confFile
334
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600335 networkconf.write ('[Match]'+ '\n')
336 networkconf.write ('Name=' + (device) + '\n')
337 networkconf.write ('[Network]' + '\n')
338 networkconf.write ('Address=' + ipaddr + '/' + str(prefixLen) + '\n')
339 networkconf.write ('Gateway=' + gateway + '\n')
Chris Austenb13a3cd2016-02-01 18:18:21 -0600340 networkconf.close()
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600341
342 print("Restarting networkd service...")
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600343 rc = call(["ip", "addr", "flush", device])
344 rc = call(["systemctl", "restart", "systemd-networkd.service"])
345 return rc
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600346
Hariharasubramanian Rcc28a6f2016-04-29 12:25:12 -0500347 @dbus.service.method(DBUS_NAME, "s", "s")
348 def GetAddressType (self, device):
349 if not self._isvaliddev (device) : raise ValueError, "Invalid Device"
350
351 confFile = "/etc/systemd/network/00-bmc-" + device + ".network"
352 if not os.path.exists(confFile):
353 print "Config file (%s) not found !" % confFile
354 netprov = network_providers [self.provider]
355 bus_name = netprov ['bus_name']
356 obj_name = netprov ['ip_object_name']
357 o = self.bus.get_object(bus_name, obj_name, introspect=False)
358 i = dbus.Interface(o, 'org.freedesktop.DBus.Properties')
359 f = i.Get (netprov ['ip_if_name'], "SourcePath")
360 print "Using default networkd config file (%s)" % f
361 confFile = f
362
363 with open(confFile, "r") as f:
364 for line in f:
365 config = line.split ("=")
366 if (len (config) < 2) : continue
367 if config [0].upper() == "DHCP":
368 v = config[1].strip().upper()
369 if (v=="YES" or v=="IPV4" or v=="IPV6"):
370 return "DHCP"
371 return "STATIC"
372
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600373 #family, prefixlen, ip, defgw
374 @dbus.service.method(DBUS_NAME, "s", "iyss")
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600375 def GetAddress4 (self, device):
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600376 if not self._isvaliddev (device) : raise ValueError, "Invalid Device"
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600377 return self._getAddr ("ip", device)
378
379 @dbus.service.method(DBUS_NAME, "s", "s")
380 def GetHwAddress (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 ("mac", device)
383
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600384 @dbus.service.method(DBUS_NAME, "ss", "i")
385 def SetHwAddress (self, device, mac):
386 if not self._isvaliddev (device) : raise ValueError, "Invalid Device"
387 if not self._ishwdev (device) : raise ValueError, "Not a Hardware Device"
Ratan Gupta8613b872016-10-07 17:15:58 +0530388
389 self.validatemac(mac)
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600390
Adriana Kobylak88c733b2016-02-03 16:46:58 -0600391 rc = subprocess.call(["fw_setenv", "ethaddr", mac])
Hariharasubramanian R308cffc2016-03-03 09:35:16 -0600392
393 print("Restarting networkd service...")
394 rc = call(["ip", "link", "set", "dev", device, "down"])
395 rc = call(["ip", "link", "set", "dev", device, "address", mac])
396 rc = call(["ip", "link", "set", "dev", device, "up"])
397
398 rc = call(["systemctl", "restart", "systemd-networkd.service"])
Adriana Kobylak88c733b2016-02-03 16:46:58 -0600399 return rc
400
vishwa5c912c82016-03-24 07:59:28 -0500401 #string of nameservers
402 @dbus.service.method(DBUS_NAME,"s", "s")
403 def SetNameServers (self, nameservers):
404 dns_entry = nameservers.split()
405 fail_msg = ''
406 dhcp_auto = False
407 file_opened = False
408 if len(dns_entry) > 0:
409 for dns in dns_entry:
Edward A. James75757c02016-07-15 09:11:18 -0500410 valid = self._isvalidip (dns)
411 if valid != "Valid":
vishwa5c912c82016-03-24 07:59:28 -0500412 if dns == "DHCP_AUTO=":
413 #This DNS is supplied by DHCP.
414 dhcp_auto = True
415 else:
Edward A. James75757c02016-07-15 09:11:18 -0500416 print valid + " DNS Address [" + dns + "]"
vishwa5c912c82016-03-24 07:59:28 -0500417 fail_msg = fail_msg + '[' + dns + ']'
418 else:
419 #Only over write on a first valid input
420 if file_opened == False:
421 resolv_conf = open("/etc/resolv.conf",'w')
422 file_opened = True
423 if dhcp_auto == True:
424 resolv_conf.write("### Generated automatically via DHCP ###\n")
425 else:
426 resolv_conf.write("### Generated manually via dbus settings ###\n")
427 dns_ip = 'nameserver ' + dns + '\n'
428 resolv_conf.write(dns_ip)
429 if file_opened == True:
430 resolv_conf.close()
431 else:
432 raise ValueError, "Invalid DNS entry"
433 if len(fail_msg) > 0:
434 return 'Failures encountered processing' + fail_msg
435 else:
436 return "DNS entries updated Successfully"
437
Hariharasubramanian Rcd387972016-01-20 07:19:41 -0600438def main():
439 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
440 bus = dbus.SystemBus()
441 name = dbus.service.BusName(DBUS_NAME, bus)
442 obj = NetMan (bus, OBJ_NAME)
443 obj.setNetworkProvider ("networkd")
444 mainloop = gobject.MainLoop()
445 print("Started")
446 mainloop.run()
447
448if __name__ == '__main__':
Adriana Kobylake150bfd2016-02-01 21:47:34 -0600449 sys.exit(main())