@@ -980,6 +980,31 @@ def get_ifname(self, netname):
980980 return c ["name" ]
981981 return None
982982
983+ def set_dummy_addr (self , cconf ):
984+ if ip := cconf .get ("ip" ):
985+ ipaddr = ipaddress .ip_interface (ip )
986+ assert ipaddr .version == 4
987+ else :
988+ ipaddr = None
989+
990+ if ip := cconf .get ("ipv6" ):
991+ ip6addr = ipaddress .ip_interface (ip )
992+ assert ip6addr .version == 6
993+ else :
994+ ip6addr = None
995+
996+ if "physical" in cconf or self .is_vm :
997+ return
998+
999+ ifname = cconf ["name" ]
1000+ for ip in (ipaddr , ip6addr ):
1001+ if ip is None :
1002+ continue
1003+ self .set_intf_addr (ifname , ip )
1004+ ipcmd = "ip " if ip .version == 4 else "ip -6 "
1005+ self .logger .debug ("%s: adding %s to unconnected intf %s" , self , ip , ifname )
1006+ self .intf_ip_cmd (ifname , ipcmd + f"addr add { ip } dev { ifname } " )
1007+
9831008 def set_lan_addr (self , switch , cconf ):
9841009 if ip := cconf .get ("ip" ):
9851010 ipaddr = ipaddress .ip_interface (ip )
@@ -1055,20 +1080,42 @@ def _set_p2p_addr(self, other, cconf, occonf, ipv6=False):
10551080 return
10561081
10571082 if ipaddr :
1083+ # Check if the two sides of this link are assigned
1084+ # different subnets. If so, set the peer address.
1085+ set_peer = False
1086+ if oipaddr and ipaddr .network != oipaddr .network :
1087+ set_peer = True
10581088 ifname = cconf ["name" ]
1059- self .set_intf_addr (ifname , ipaddr )
1089+ self .set_intf_addr (ifname , ipaddr , oipaddr )
10601090 self .logger .debug ("%s: adding %s to p2p intf %s" , self , ipaddr , ifname )
10611091 if "physical" not in cconf and not self .is_vm :
1062- self .intf_ip_cmd (ifname , f"ip addr add { ipaddr } dev { ifname } " )
1092+ if set_peer :
1093+ self .logger .debug ("%s: setting peer address %s" , self , oipaddr )
1094+ self .intf_ip_cmd (
1095+ ifname ,
1096+ f"ip addr add { ipaddr .ip } peer { oipaddr .network } dev { ifname } " ,
1097+ )
1098+ else :
1099+ self .intf_ip_cmd (ifname , f"ip addr add { ipaddr } dev { ifname } " )
10631100
10641101 if oipaddr :
1102+ set_peer = False
1103+ if ipaddr and ipaddr .network != oipaddr .network :
1104+ set_peer = True
10651105 oifname = occonf ["name" ]
1066- other .set_intf_addr (oifname , oipaddr )
1106+ other .set_intf_addr (oifname , oipaddr , ipaddr )
10671107 self .logger .debug (
10681108 "%s: adding %s to other p2p intf %s" , other , oipaddr , oifname
10691109 )
10701110 if "physical" not in occonf and not other .is_vm :
1071- other .intf_ip_cmd (oifname , f"ip addr add { oipaddr } dev { oifname } " )
1111+ if set_peer :
1112+ other .logger .debug ("%s: setting peer address %s" , other , ipaddr )
1113+ other .intf_ip_cmd (
1114+ oifname ,
1115+ f"ip addr add { oipaddr .ip } peer { ipaddr .network } dev { oifname } " ,
1116+ )
1117+ else :
1118+ other .intf_ip_cmd (oifname , f"ip addr add { oipaddr } dev { oifname } " )
10721119
10731120 def set_p2p_addr (self , other , cconf , occonf ):
10741121 self ._set_p2p_addr (other , cconf , occonf , ipv6 = False )
@@ -2225,13 +2272,33 @@ async def renumber_interfaces(self):
22252272 con .cmd_raises (f"ip -4 addr flush dev { ifname } " )
22262273 sw_is_nat = switch and hasattr (switch , "is_nat" ) and switch .is_nat
22272274 if ifaddr := self .get_intf_addr (ifname , ipv6 = False ):
2228- con .cmd_raises (f"ip addr add { ifaddr } dev { ifname } " )
2275+ oifaddr = self .get_peer_intf_addr (ifname , ipv6 = False )
2276+ if (
2277+ not switch
2278+ and oifaddr is not None
2279+ and ifaddr .network != oifaddr .network
2280+ ):
2281+ con .cmd_raises (
2282+ f"ip addr add { ifaddr .ip } peer { oifaddr .network } dev { ifname } "
2283+ )
2284+ else :
2285+ con .cmd_raises (f"ip addr add { ifaddr } dev { ifname } " )
22292286 if sw_is_nat :
22302287 # In case there was some preconfig e.g., cloud-init
22312288 con .cmd_raises ("ip route flush exact default" )
22322289 con .cmd_raises (f"ip route add default via { switch .ip_address } " )
22332290 if ifaddr := self .get_intf_addr (ifname , ipv6 = True ):
2234- con .cmd_raises (f"ip -6 addr add { ifaddr } dev { ifname } " )
2291+ oifaddr = self .get_peer_intf_addr (ifname , ipv6 = True )
2292+ if (
2293+ not switch
2294+ and oifaddr is not None
2295+ and ifaddr .network != oifaddr .network
2296+ ):
2297+ con .cmd_raises (
2298+ f"ip addr add { ifaddr .ip } peer { oifaddr .network } dev { ifname } "
2299+ )
2300+ else :
2301+ con .cmd_raises (f"ip -6 addr add { ifaddr } dev { ifname } " )
22352302 if sw_is_nat :
22362303 # In case there was some preconfig e.g., cloud-init
22372304 con .cmd_raises ("ip -6 route flush exact default" )
@@ -3204,8 +3271,9 @@ async def _async_build(self, logger=None):
32043271 if "connections" not in nconf :
32053272 continue
32063273 for cconf in nconf ["connections" ]:
3207- # Eventually can add support for unconnected intf here.
32083274 if "to" not in cconf :
3275+ # unconnected intf
3276+ await self .add_dummy_link (node , cconf )
32093277 continue
32103278 to = cconf ["to" ]
32113279 if to in self .switches :
@@ -3236,6 +3304,25 @@ def autonumber(self):
32363304 def autonumber (self , value ):
32373305 self .topoconf ["networks-autonumber" ] = bool (value )
32383306
3307+ async def add_dummy_link (self , node1 , c1 = None ):
3308+ c1 = {} if c1 is None else c1
3309+
3310+ if "name" not in c1 :
3311+ c1 ["name" ] = node1 .get_next_intf_name ()
3312+ if1 = c1 ["name" ]
3313+
3314+ do_add_dummy = True
3315+ if "hostintf" in c1 :
3316+ await node1 .add_host_intf (c1 ["hostintf" ], c1 ["name" ], mtu = c1 .get ("mtu" ))
3317+ do_add_dummy = False
3318+ elif "physical" in c1 :
3319+ await node1 .add_phy_intf (c1 ["physical" ], c1 ["name" ])
3320+ do_add_dummy = False
3321+
3322+ if do_add_dummy :
3323+ super ().add_dummy (node1 , if1 , ** c1 )
3324+ node1 .set_dummy_addr (c1 )
3325+
32393326 async def add_native_link (self , node1 , node2 , c1 = None , c2 = None ):
32403327 """Add a link between switch and node or 2 nodes."""
32413328 isp2p = False
0 commit comments