]> git.proxmox.com Git - mirror_ifupdown2.git/blobdiff - ifupdown2/addons/address.py
Merge pull request #121 from aderumier/arpaccept
[mirror_ifupdown2.git] / ifupdown2 / addons / address.py
index 758a49e4453741244a29bab1433bdf0cdf85ddda..a8ef1e25fe38a5132bc2443c54fb5392ce7295a9 100644 (file)
@@ -96,14 +96,19 @@ class address(moduleBase):
                               'dual connected VxLANs',
                               'validvals' : ['<ipv4>', ],
                               'example'  : ['clagd-vxlan-anycast-ip 36.0.0.11']},
+                      'arp-accept' :
+                            { 'help': 'Allow gratuitous arp to update arp table',
+                              'validvals': ['on', 'off', 'yes', 'no', '0', '1'],
+                              'default' : 'off',
+                              'example' : ['arp-accept on']},
                       'ip-forward' :
                             { 'help': 'ip forwarding flag',
-                              'validvals': ['on', 'off'],
+                              'validvals': ['on', 'off', 'yes', 'no', '0', '1'],
                               'default' : 'off',
                               'example' : ['ip-forward off']},
                       'ip6-forward' :
                             { 'help': 'ipv6 forwarding flag',
-                              'validvals': ['on', 'off'],
+                              'validvals': ['on', 'off', 'yes', 'no', '0', '1'],
                               'default' : 'off',
                               'example' : ['ip6-forward off']},
                       'mpls-enable' :
@@ -148,6 +153,22 @@ class address(moduleBase):
 
         self.lower_iface_mtu_checked_list = list()
 
+        self.l3_intf_arp_accept = utils.get_boolean_from_string(
+            policymanager.policymanager_api.get_module_globals(
+                module_name=self.__class__.__name__,
+                attr='l3_intf_arp_accept'
+            ),
+            default=False
+        )
+
+        self.l3_intf_default_gateway_set_onlink = utils.get_boolean_from_string(
+            policymanager.policymanager_api.get_module_globals(
+                module_name=self.__class__.__name__,
+                attr='l3_intf_default_gateway_set_onlink'
+            ),
+            default=True
+        )
+
     def syntax_check(self, ifaceobj, ifaceobj_getfunc=None):
         return (self.syntax_check_multiple_gateway(ifaceobj)
                 and self.syntax_check_addr_allowed_on(ifaceobj, True)
@@ -256,6 +277,8 @@ class address(moduleBase):
     def _process_bridge(self, ifaceobj, up):
         hwaddress = self._get_hwaddress(ifaceobj)
         addrs = ifaceobj.get_attr_value_first('address')
+        arp_accept = ifaceobj.get_attr_value_first('arp-accept')
+        arp_accept = utils.boolean_support_binary(arp_accept)
         is_vlan_dev_on_vlan_aware_bridge = False
         is_bridge = self.ipcmd.is_bridge(ifaceobj.name)
         if not is_bridge:
@@ -265,18 +288,30 @@ class address(moduleBase):
                 is_vlan_dev_on_vlan_aware_bridge = self.ipcmd.bridge_is_vlan_aware(bridgename)
         if ((is_bridge and not self.ipcmd.bridge_is_vlan_aware(ifaceobj.name))
                         or is_vlan_dev_on_vlan_aware_bridge):
-           if self._address_valid(addrs):
-              if up:
-                self.write_file('/proc/sys/net/ipv4/conf/%s' %ifaceobj.name +
-                                '/arp_accept', '1')
-              else:
-                self.write_file('/proc/sys/net/ipv4/conf/%s' %ifaceobj.name +
-                                '/arp_accept', '0')
+            if self._address_valid(addrs):
+                if self.l3_intf_arp_accept:
+                    if up:
+                        self.write_file('/proc/sys/net/ipv4/conf/%s' % ifaceobj.name +
+                                        '/arp_accept', '1')
+                    else:
+                        self.write_file('/proc/sys/net/ipv4/conf/%s' % ifaceobj.name +
+                                        '/arp_accept', '0')
+                else:
+                    self.write_file('/proc/sys/net/ipv4/conf/%s/arp_accept' % ifaceobj.name, arp_accept)
         if hwaddress and is_vlan_dev_on_vlan_aware_bridge:
-           if up:
-              self.ipcmd.bridge_fdb_add(bridgename, hwaddress, vlan)
-           else:
-              self.ipcmd.bridge_fdb_del(bridgename, hwaddress, vlan)
+            if up:
+                # check statemanager to delete the old entry if necessary
+                try:
+                    for old_obj in statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name) or []:
+                        old_hwaddress = old_obj.get_attr_value_first("hwaddress")
+                        if old_hwaddress and self.ipcmd.mac_str_to_int(old_hwaddress) != self.ipcmd.mac_str_to_int(hwaddress):
+                            self.ipcmd.bridge_fdb_del(bridgename, old_hwaddress, vlan)
+                            break
+                except:
+                    pass
+                self.ipcmd.bridge_fdb_add(bridgename, hwaddress, vlan)
+            else:
+                self.ipcmd.bridge_fdb_del(bridgename, hwaddress, vlan)
 
     def _get_anycast_addr(self, ifaceobjlist):
         for ifaceobj in ifaceobjlist:
@@ -487,7 +522,7 @@ class address(moduleBase):
                              vrf, metric)
         for add_gw in gateways:
             try:
-                self.ipcmd.route_add_gateway(ifaceobj.name, add_gw, vrf, metric)
+                self.ipcmd.route_add_gateway(ifaceobj.name, add_gw, vrf, metric, onlink=self.l3_intf_default_gateway_set_onlink)
             except Exception as e:
                 self.log_error('%s: %s' % (ifaceobj.name, str(e)))
 
@@ -584,10 +619,12 @@ class address(moduleBase):
             return
 
         if ifaceobj.link_kind:
-            # bonds and vxlan devices need an explicit set of mtu.
+            # bonds, vxlan and custom devices (like dummy) need an explicit set of mtu.
             # bridges don't need mtu set
             if (ifaceobj.link_kind & ifaceLinkKind.BOND or
-                ifaceobj.link_kind & ifaceLinkKind.VXLAN):
+                ifaceobj.link_kind & ifaceLinkKind.VXLAN or
+                ifaceobj.link_kind & ifaceLinkKind.OTHER
+            ):
                 running_mtu = self.ipcmd.link_get_mtu(ifaceobj.name)
                 if (self.default_mtu and running_mtu != self.default_mtu):
                     self.ipcmd.link_set(ifaceobj.name, 'mtu', self.default_mtu)
@@ -625,17 +662,23 @@ class address(moduleBase):
     def _set_bridge_forwarding(self, ifaceobj):
         """ set ip forwarding to 0 if bridge interface does not have a
         ip nor svi """
+        ifname = ifaceobj.name
         if not ifaceobj.upperifaces and not ifaceobj.get_attr_value('address'):
-            # set forwarding = 0
-            if self.sysctl_get('net.ipv4.conf.%s.forwarding' %ifaceobj.name) == '1':
-                self.sysctl_set('net.ipv4.conf.%s.forwarding' %ifaceobj.name, 0)
-            if self.sysctl_get('net.ipv6.conf.%s.forwarding' %ifaceobj.name) == '1':
-                self.sysctl_set('net.ipv6.conf.%s.forwarding' %ifaceobj.name, 0)
+            if self.sysctl_get_forwarding_value_from_proc(ifname, "ipv4") == '1':
+                self.sysctl_write_forwarding_value_to_proc(ifname, "ipv4", 0)
+            if self.sysctl_get_forwarding_value_from_proc(ifname, "ipv6") == '1':
+                self.sysctl_write_forwarding_value_to_proc(ifname, "ipv6", 0)
         else:
-            if self.sysctl_get('net.ipv4.conf.%s.forwarding' %ifaceobj.name) == '0':
-                self.sysctl_set('net.ipv4.conf.%s.forwarding' %ifaceobj.name, 1)
-            if self.sysctl_get('net.ipv6.conf.%s.forwarding' %ifaceobj.name) == '0':
-                self.sysctl_set('net.ipv6.conf.%s.forwarding' %ifaceobj.name, 1)
+            if self.sysctl_get_forwarding_value_from_proc(ifname, "ipv4") == '0':
+                self.sysctl_write_forwarding_value_to_proc(ifname, "ipv4", 1)
+            if self.sysctl_get_forwarding_value_from_proc(ifname, "ipv6") == '0':
+                self.sysctl_write_forwarding_value_to_proc(ifname, "ipv6", 1)
+
+    def sysctl_get_forwarding_value_from_proc(self, ifname, family):
+        return self.read_file_oneline("/proc/sys/net/%s/conf/%s/forwarding" % (family, ifname))
+
+    def sysctl_write_forwarding_value_to_proc(self, ifname, family, value):
+        self.write_file("/proc/sys/net/%s/conf/%s/forwarding" % (family, ifname), "%s\n" % value)
 
     def _sysctl_config(self, ifaceobj):
         setting_default_value = False
@@ -690,51 +733,57 @@ class address(moduleBase):
                 self.log_error('%s: \'ip6-forward\' is not supported for '
                                'bridge port' %ifaceobj.name)
             return
+
         setting_default_value = False
         if not ipforward:
             setting_default_value = True
-            ipforward = (self.ipforward or
-                         self.get_mod_subattr('ip-forward', 'default'))
-        ipforward = utils.boolean_support_binary(ipforward)
-        # File read has been used for better performance
-        # instead of using sysctl command
-        running_ipforward = self.read_file_oneline(
-                                '/proc/sys/net/ipv4/conf/%s/forwarding'
-                                %ifaceobj.name)
-        if ipforward != running_ipforward:
-            try:
-                self.sysctl_set('net.ipv4.conf.%s.forwarding'
-                                %('/'.join(ifaceobj.name.split("."))),
-                                ipforward)
-            except Exception as e:
-                if not setting_default_value:
-                    ifaceobj.status = ifaceStatus.ERROR
-                    self.logger.error('%s: %s' %(ifaceobj.name, str(e)))
+            ipforward = self.ipforward
+
+        if ipforward:
+            ipforward = utils.boolean_support_binary(ipforward)
+            # File read has been used for better performance
+            # instead of using sysctl command
+            running_ipforward = self.read_file_oneline(
+                                    '/proc/sys/net/ipv4/conf/%s/forwarding'
+                                   %ifaceobj.name)
+
+            if ipforward != running_ipforward:
+                try:
+
+                    self.sysctl_set('net.ipv4.conf.%s.forwarding'
+                                    %('/'.join(ifaceobj.name.split("."))),
+                                    ipforward)
+                except Exception as e:
+                    if not setting_default_value:
+                        ifaceobj.status = ifaceStatus.ERROR
+                        self.logger.error('%s: %s' %(ifaceobj.name, str(e)))
+
 
         setting_default_value = False
         if not ip6forward:
             setting_default_value = True
-            ip6forward = (self.ip6forward or
-                          self.get_mod_subattr('ip6-forward', 'default'))
-        ip6forward = utils.boolean_support_binary(ip6forward)
-        # File read has been used for better performance
-        # instead of using sysctl command
-        running_ip6forward = self.read_file_oneline(
-                                '/proc/sys/net/ipv6/conf/%s/forwarding'
-                                %ifaceobj.name)
-        if ip6forward != running_ip6forward:
-            try:
-                self.sysctl_set('net.ipv6.conf.%s.forwarding'
-                                %('/'.join(ifaceobj.name.split("."))),
-                                ip6forward)
-            except Exception as e:
-                # There is chance of ipv6 being removed because of,
-                # for example, setting mtu < 1280
-                # In such cases, log error only if user has configured
-                # ip6-forward
-                if not setting_default_value:
-                    ifaceobj.status = ifaceStatus.ERROR
-                    self.logger.error('%s: %s' %(ifaceobj.name, str(e)))
+            ip6forward = self.ip6forward
+
+        if ip6forward:
+            ip6forward = utils.boolean_support_binary(ip6forward)
+            # File read has been used for better performance
+            # instead of using sysctl command
+            running_ip6forward = self.read_file_oneline(
+                                    '/proc/sys/net/ipv6/conf/%s/forwarding'
+                                    %ifaceobj.name)
+            if ip6forward != running_ip6forward:
+                try:
+                    self.sysctl_set('net.ipv6.conf.%s.forwarding'
+                                    %('/'.join(ifaceobj.name.split("."))),
+                                    ip6forward)
+                except Exception as e:
+                    # There is chance of ipv6 being removed because of,
+                    # for example, setting mtu < 1280
+                    # In such cases, log error only if user has configured
+                    # ip6-forward
+                    if not setting_default_value:
+                        ifaceobj.status = ifaceStatus.ERROR
+                        self.logger.error('%s: %s' %(ifaceobj.name, str(e)))
 
     def process_mtu(self, ifaceobj, ifaceobj_getfunc):
         mtu = ifaceobj.get_attr_value_first('mtu')
@@ -755,17 +804,16 @@ class address(moduleBase):
     def up_ipv6_addrgen(self, ifaceobj):
         user_configured_ipv6_addrgen = ifaceobj.get_attr_value_first('ipv6-addrgen')
 
-        if not user_configured_ipv6_addrgen:
-            for old_ifaceobj in statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name) or []:
-                old_config_ipv6_addrgen = old_ifaceobj.get_attr_value_first('ipv6-addrgen')
+        if not user_configured_ipv6_addrgen and ifupdownflags.flags.PERFMODE:
+            # no need to go further during perfmode (boot)
+            return
 
-                if old_config_ipv6_addrgen:
-                    user_configured_ipv6_addrgen = self.get_attr_default_value('ipv6-addrgen')
-                    break
+        if not user_configured_ipv6_addrgen and ifaceobj.addr_method == 'dhcp':
+            return
 
-            if not user_configured_ipv6_addrgen:
-                # no previous config detected we dont have to configure ipv6-addrgen
-                return
+        if not user_configured_ipv6_addrgen:
+            # if user didn't configure ipv6-addrgen, should we reset to default?
+            user_configured_ipv6_addrgen = self.get_attr_default_value('ipv6-addrgen')
 
         ipv6_addrgen_nl = {
             'on': 0,
@@ -824,6 +872,12 @@ class address(moduleBase):
         if addr_method not in ["dhcp", "ppp"]:
             self._inet_address_config(ifaceobj, ifaceobj_getfunc,
                                       force_reapply)
+        else:
+            # remove old addresses added by ifupdown2
+            # (if intf was moved from static config to dhcp)
+            for old_ifaceobj in statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name) or []:
+                for addr in old_ifaceobj.get_attr_value("address") or []:
+                    self.ipcmd.addr_del(ifaceobj.name, addr)
 
         self.process_mtu(ifaceobj, ifaceobj_getfunc)
 
@@ -832,13 +886,25 @@ class address(moduleBase):
         except Exception as e:
             self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj, raise_error=False)
 
+        self.up_hwaddress(ifaceobj)
+
+        gateways = ifaceobj.get_attr_value('gateway')
+        if not gateways:
+            gateways = []
+        prev_gw = self._get_prev_gateway(ifaceobj, gateways)
+        self._add_delete_gateway(ifaceobj, gateways, prev_gw)
+
+    def up_hwaddress(self, ifaceobj):
         try:
             hwaddress = self._get_hwaddress(ifaceobj)
+
             if hwaddress:
-                running_hwaddress = None
-                if not ifupdownflags.flags.PERFMODE: # system is clean
+                if not ifupdownflags.flags.PERFMODE:  # system is clean
                     running_hwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name)
-                if hwaddress != running_hwaddress:
+                else:
+                    running_hwaddress = None
+
+                if self.ipcmd.mac_str_to_int(running_hwaddress) != self.ipcmd.mac_str_to_int(hwaddress):
                     slave_down = False
                     netlink.link_set_updown(ifaceobj.name, "down")
                     if ifaceobj.link_kind & ifaceLinkKind.BOND:
@@ -848,7 +914,7 @@ class address(moduleBase):
                                 netlink.link_set_updown(l, "down")
                             slave_down = True
                     try:
-                        self.ipcmd.link_set(ifaceobj.name, 'address', hwaddress)
+                        self.ipcmd.link_set_hwaddress(ifaceobj.name, hwaddress, force=True)
                     finally:
                         netlink.link_set_updown(ifaceobj.name, "up")
                         if slave_down:
@@ -858,13 +924,7 @@ class address(moduleBase):
             # Handle special things on a bridge
             self._process_bridge(ifaceobj, True)
         except Exception, e:
-            self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
-
-        gateways = ifaceobj.get_attr_value('gateway')
-        if not gateways:
-            gateways = []
-        prev_gw = self._get_prev_gateway(ifaceobj, gateways)
-        self._add_delete_gateway(ifaceobj, gateways, prev_gw)
+            self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
 
     def _down(self, ifaceobj, ifaceobj_getfunc=None):
         try:
@@ -933,9 +993,12 @@ class address(moduleBase):
             bridgename = ifaceobj.lowerifaces[0]
             vlan = self._get_vlan_id(ifaceobj)
             if self.ipcmd.bridge_is_vlan_aware(bridgename):
-                fdb_addrs = self._get_bridge_fdbs(bridgename, str(vlan))
-                if not fdb_addrs or hwaddress not in fdb_addrs:
-                   return False
+                fdb_addrs = [self.ipcmd.mac_str_to_int(fdb_addr) for fdb_addr in self._get_bridge_fdbs(bridgename, str(vlan))]
+                if not fdb_addrs:
+                    return False
+                hwaddress_int = self.ipcmd.mac_str_to_int(hwaddress)
+                if hwaddress_int not in fdb_addrs:
+                    return False
         return True
 
     def _query_sysctl(self, ifaceobj, ifaceobjcurr):
@@ -1018,7 +1081,7 @@ class address(moduleBase):
         hwaddress = self._get_hwaddress(ifaceobj)
         if hwaddress:
             rhwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name)
-            if not rhwaddress  or rhwaddress != hwaddress:
+            if not rhwaddress or self.ipcmd.mac_str_to_int(rhwaddress) != self.ipcmd.mac_str_to_int(hwaddress):
                ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
                        1)
             elif not self._check_addresses_in_bridge(ifaceobj, hwaddress):