Skip to content

Commit

Permalink
Introduced bridge link show command
Browse files Browse the repository at this point in the history
  • Loading branch information
signal-09 committed Sep 10, 2024
1 parent 5ce0728 commit d5e872b
Show file tree
Hide file tree
Showing 3 changed files with 258 additions and 36 deletions.
72 changes: 69 additions & 3 deletions iproute4mac/brlink.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import iproute4mac.ifconfig as ifconfig
import iproute4mac.libc as libc
import iproute4mac.utils as utils

from iproute4mac.ipaddress import get_ifconfig_links
from iproute4mac import OPTION
from iproute4mac.utils import matches, strcmp, next_arg


Expand Down Expand Up @@ -31,12 +32,74 @@ def usage():


def brlink_modify(argv):
dev = None
while argv:
opt = argv.pop(0)
if strcmp(opt, "dev"):
dev = next_arg(argv)
elif strcmp(opt, "guard"):
guard = next_arg(argv)
utils.parse_on_off(opt, guard)
utils.do_notimplemented(opt)
elif strcmp(opt, "hairpin"):
hairpin = next_arg(argv)
utils.parse_on_off(opt, hairpin)
utils.do_notimplemented(opt)
elif strcmp(opt, "fastleave"):
fastleave = next_arg(argv)
utils.parse_on_off(opt, fastleave)
utils.do_notimplemented(opt)
elif strcmp(opt, "root_block"):
root_block = next_arg(argv)
utils.parse_on_off(opt, root_block)
utils.do_notimplemented(opt)
elif strcmp(opt, "learning"):
learning = next_arg(argv)
utils.parse_on_off(opt, learning)
utils.do_notimplemented(opt)
elif strcmp(opt, "learning_sync"):
learning_sync = next_arg(argv)
utils.parse_on_off(opt, learning_sync)
utils.do_notimplemented(opt)
elif strcmp(opt, "flood"):
flood = next_arg(argv)
utils.parse_on_off(opt, flood)
utils.do_notimplemented(opt)
elif strcmp(opt, "mcast_flood"):
mcast_flood = next_arg(argv)
utils.parse_on_off(opt, mcast_flood)
utils.do_notimplemented(opt)
elif strcmp(opt, "mcast_to_unicast"):
mcast_to_unicast = next_arg(argv)
utils.parse_on_off(opt, mcast_to_unicast)
utils.do_notimplemented(opt)
elif strcmp(opt, "cost"):
cost = next_arg(argv)
try:
cost = int(cost)
except ValueError:
utils.invarg(f'Invalid "{opt}" value', cost)
utils.do_notimplemented(opt)
elif strcmp(opt, "priority"):
priority = next_arg(argv)
try:
priority = int(priority)
except ValueError:
utils.invarg(f'Invalid "{opt}" value', priority)
utils.do_notimplemented(opt)
else:
usage()

if not dev:
utils.stderr("Device is a required argument.")
exit(libc.EXIT_ERROR)

return libc.EXIT_SUCCESS


def brlink_list(argv):
dev = None
links = get_ifconfig_links(argv)
links = ifconfig.Bridge()
while argv:
opt = argv.pop(0)
if strcmp(opt, "dev"):
Expand All @@ -47,7 +110,10 @@ def brlink_list(argv):
utils.stderr(f'Cannot find device "{opt}"')
exit(libc.EXIT_ERROR)
dev = opt
links.set([l for l in links if l.ifname == dev])
links.set([i for i in links if i.name == dev])

if not OPTION["show_details"]:
links.set([i for i in links if i.link != i])

utils.output(links)

Expand Down
193 changes: 160 additions & 33 deletions iproute4mac/ifconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,6 @@
# map operstates
OPER_STATES = {"active": "UP", "inactive": "DOWN", "none": "UNKNOWN"}

_OPTIONAL_FIELDS = [
"eflags",
"xflags",
"options",
"capabilities",
"hwassist",
"promiscuity",
"min_mtu",
"max_mtu",
"linkinfo",
"num_tx_queues",
"num_rx_queues",
"gso_max_size",
"gso_max_segs",
]

# MAC address RegEx
LLSEG = "[0-9a-fA-F]{1,2}"
LLADDR = "(?:%s(?::%s){5})" % (LLSEG, LLSEG)
Expand Down Expand Up @@ -187,6 +171,21 @@ class _IfconfigBase:
_name = None
_data = None
_link = None
_OPTIONAL_FIELDS = [
"eflags",
"xflags",
"options",
"capabilities",
"hwassist",
"promiscuity",
"min_mtu",
"max_mtu",
"linkinfo",
"num_tx_queues",
"num_rx_queues",
"gso_max_size",
"gso_max_segs",
]

def __init__(self):
raise NotImplementedError
Expand Down Expand Up @@ -233,7 +232,7 @@ def data(self, details=True):
for key, value in self._data.items():
if value is None:
continue
if key not in _OPTIONAL_FIELDS or details:
if key not in self._OPTIONAL_FIELDS or details:
res[key] = value
return res

Expand Down Expand Up @@ -315,6 +314,14 @@ class _Ifconfig(_IfconfigBase):
_routermode4 = re.compile(r"\troutermode4: (?P<routermode4>\w+)")
_routermode6 = re.compile(r"\troutermode6: (?P<routermode6>\w+)")

@property
def link(self):
if self._data.get("peer"):
return self._data["peer"]
if self._data.get("vlan", {}).get("parent"):
return self._data["vlan"]["parent"]
return None

def __init__(self, text):
(self._name, text) = _reDict(self._name_data, text).groups()

Expand All @@ -341,7 +348,7 @@ def __init__(self, text):
"bond": _reBond(self._bond, text).data,
**_reDict(self._generation_id, text).data,
**_reDict(self._type, text).data,
"link_type": "none",
"link_type": "unknown",
"agent": _reList(self._agent, text).data,
"link_quality": _reDict(self._link_quality, text).data,
"state_availability": _reDict(self._state_availability, text).data,
Expand All @@ -365,6 +372,8 @@ def __init__(self, text):
self._data["link_type"] = "loopback"
self._data["ether"] = "00:00:00:00:00:00"
self._data["broadcast"] = "00:00:00:00:00:00"
elif "POINTOPOINT" in self._data["flags"]:
self._data["link_type"] = "none"

def __iter__(self):
for key, value in self._data:
Expand Down Expand Up @@ -538,7 +547,7 @@ def __init__(self, text):
self._name = self._ifconfig._name
self._data = {
"ifindex": self._ifconfig["index"],
"link": None, # async __update__ with self._get_link()
"link": self._ifconfig.link,
"ifname": self._name,
"flags": self._ifconfig["flags"],
"eflags": self._ifconfig.get("eflags", {}).get("flags"),
Expand Down Expand Up @@ -572,19 +581,11 @@ def __init__(self, text):
def __update__(self):
self._data.update(
{
"link": self._get_link(),
"master": self._get_master(),
"linkinfo": self._get_linkinfo(),
}
)

def _get_link(self):
if self._ifconfig.get("peer"):
return self._ifconfig["peer"]
if self._ifconfig.get("vlan", {}).get("parent"):
return self._ifconfig["vlan"]["parent"]
return None

def _get_master(self):
if self.link:
return self.link.name
Expand Down Expand Up @@ -658,10 +659,48 @@ def _get_info_slave_kind(self):
return None

def _get_info_slave_data(self):
if self._ifconfig.link:
if self._ifconfig.link.name.startswith("bond"):
if self.link:
if self.link.name.startswith("bond"):
# FIXME: where to find original hardawre lladdr?
return {"perm_hwaddr": self._ifconfig["ether"]}
if self.link.name.startswith("bridge"):
bridge = self.link._ifconfig._data["bridge"]
if member := next(
(member for member in bridge["member"] if member["interface"] == self._name),
None,
):
return {
"state": "forwarding",
"priority": int(member["priority"]),
"cost": int(member["cost"]),
# "hairpin": False,
# "guard": False,
# "root_block": False,
# "fastleave": False,
# "learning": True,
# "flood": True,
# "id": "0x8003",
# "no": "0x3",
"designated_port": int(member["port"]),
"designated_cost": int(member["cost"]),
"bridge_id": bridge["id"],
"root_id": bridge["root_id"],
# "hold_timer": 0.00,
# "message_age_timer": 0.00,
# "forward_delay_timer": 0.00,
# "topology_change_ack": 0,
# "config_pending": 0,
# "proxy_arp": False,
# "proxy_arp_wifi": False,
# "multicast_router": 1,
# "mcast_flood": True,
# "mcast_to_unicast": False,
# "neigh_suppress": False,
# "group_fwd_mask": "0",
# "group_fwd_mask_str": "0x0",
# "vlan_tunnel": False,
# "isolated": False,
}
return None

def _get_linkinfo(self):
Expand Down Expand Up @@ -789,6 +828,75 @@ def str(self, details=None):
return res.rstrip()


class _Bridge(_IfconfigBase):
__slots__ = ("_ifconfig",)

_OPTIONAL_FIELDS = [
"hairpin",
"guard",
"root_block",
"fastleave",
"learning",
"flood",
"mcast_flood",
"mcast_to_unicast",
"neigh_suppress",
"vlan_tunnel",
"isolated",
]

def __init__(self, text):
self._ifconfig = _Ifconfig(text)
self._name = self._ifconfig._name
self._data = {
"ifindex": self._ifconfig["index"],
"link": self._ifconfig.link,
"ifname": self._name,
"flags": self._ifconfig["flags"],
"mtu": self._ifconfig["mtu"],
"master": None, # async __update__ with self._get_master()
}

def __update__(self):
if self.link == self:
self._data["master"] = self._name
else:
bridge = self.link._ifconfig._data["bridge"]
member = next(
member for member in bridge["member"] if member["interface"] == self._name
)
self._data.update(
{
"master": self.link.name,
"state": "forwarding", # FIXME: how to check the state?
"priority": int(member["priority"]),
"cost": int(member["cost"]),
# "hairpin": False,
# "guard": False,
# "root_block": False,
# "fastleave": False,
# "learning": True,
# "flood": True,
# "mcast_flood": True,
# "mcast_to_unicast": False,
# "neigh_suppress": False,
# "vlan_tunnel": False,
# "isolated": False,
}
)

def str(self, details=None):
data = self.data(details=details)
res = utils.dict_format(data, "{}: {}", "ifindex", "ifname")
res += utils.dict_format(data, "@{}", "link")
res += f": <{','.join(self['flags'])}> mtu {self['mtu']}"
res += utils.dict_format(data, " master {}", "master")
res += utils.dict_format(data, " state {}", "state")
res += utils.dict_format(data, " priority {}", "priority")
res += utils.dict_format(data, " cost {}", "cost")
return res


class Ifconfig:
__slots__ = "_interfaces"
_kind = _Ifconfig
Expand Down Expand Up @@ -861,11 +969,30 @@ def _link_interfaces(self):
for index, member in enumerate(interface._ifconfig.get("bond", [])):
if member is None:
continue
if isinstance(member, str):
self.lookup("ifname", member).link = interface
self.lookup("ifname", member).link = interface
if interface._ifconfig.get("bridge"):
for member in interface._ifconfig["bridge"].get("member", []):
self.lookup("ifname", member["interface"]).link = interface
for interface in self._interfaces:
interface.__update__()


class Bridge(Ifconfig):
_kind = _Bridge

def __init__(self):
super().__init__()
self._link_interfaces()

def _link_interfaces(self):
"""
Look up for bridge interface relations
"""
for interface in self._interfaces:
if interface._ifconfig.get("bridge"):
interface.link = interface
for member in interface._ifconfig["bridge"].get("member", []):
if isinstance(member["interface"], str):
self.lookup("ifname", member["interface"]).link = interface
self.lookup("ifname", member["interface"]).link = interface
self._interfaces = [interface for interface in self._interfaces if interface.link]
for interface in self._interfaces:
interface.__update__()
Loading

0 comments on commit d5e872b

Please sign in to comment.