diff --git a/iproute4mac/ipaddress.py b/iproute4mac/ipaddress.py index 724eb43..157132c 100644 --- a/iproute4mac/ipaddress.py +++ b/iproute4mac/ipaddress.py @@ -41,32 +41,14 @@ def usage(): exit(-1) -# ip address [ show [ dev IFNAME ] [ scope SCOPE-ID ] [ master DEVICE ] -# [ type TYPE ] [ to PREFIX ] [ FLAG-LIST ] -# [ label LABEL ] [up] [ vrf NAME ] ] -# SCOPE-ID := [ host | link | global | NUMBER ] -# FLAG-LIST := [ FLAG-LIST ] FLAG -# FLAG := [ permanent | dynamic | secondary | primary | -# [-]tentative | [-]deprecated | [-]dadfailed | temporary | -# CONFFLAG-LIST ] -# CONFFLAG-LIST := [ CONFFLAG-LIST ] CONFFLAG -# CONFFLAG := [ home | nodad | mngtmpaddr | noprefixroute | autojoin ] -# TYPE := { bareudp | bond | bond_slave | bridge | bridge_slave | -# dummy | erspan | geneve | gre | gretap | ifb | -# ip6erspan | ip6gre | ip6gretap | ip6tnl | -# ipip | ipoib | ipvlan | ipvtap | -# macsec | macvlan | macvtap | -# netdevsim | nlmon | rmnet | sit | team | team_slave | -# vcan | veth | vlan | vrf | vti | vxcan | vxlan | wwan | -# xfrm } -def ipaddr_list(argv, usage=usage): +def get_ifconfig_links(argv, usage=usage): res = ifconfig.exec("-v", "-L", "-a") links = ifconfig.parse(res) while argv: opt = argv.pop(0) if strcmp(opt, "to"): # to = next_arg(argv) - # get_prefix(to, OPTION['preferred_family']) + # get_prefix(to, OPTION["preferred_family"]) do_notimplemented() elif strcmp(opt, "scope"): scope = next_arg(argv) @@ -121,13 +103,34 @@ def ipaddr_list(argv, usage=usage): if not OPTION["show_details"]: delete_keys(links, "linkinfo") + return links + + +# ip address [ show [ dev IFNAME ] [ scope SCOPE-ID ] [ master DEVICE ] +# [ type TYPE ] [ to PREFIX ] [ FLAG-LIST ] +# [ label LABEL ] [up] [ vrf NAME ] ] +# SCOPE-ID := [ host | link | global | NUMBER ] +# FLAG-LIST := [ FLAG-LIST ] FLAG +# FLAG := [ permanent | dynamic | secondary | primary | +# [-]tentative | [-]deprecated | [-]dadfailed | temporary | +# CONFFLAG-LIST ] +# CONFFLAG-LIST := [ CONFFLAG-LIST ] CONFFLAG +# CONFFLAG := [ home | nodad | mngtmpaddr | noprefixroute | autojoin ] +# TYPE := { bareudp | bond | bond_slave | bridge | bridge_slave | +# dummy | erspan | geneve | gre | gretap | ifb | +# ip6erspan | ip6gre | ip6gretap | ip6tnl | +# ipip | ipoib | ipvlan | ipvtap | +# macsec | macvlan | macvtap | +# netdevsim | nlmon | rmnet | sit | team | team_slave | +# vcan | veth | vlan | vrf | vti | vxcan | vxlan | wwan | +# xfrm } +def ipaddr_list(argv): + links = get_ifconfig_links(argv) if OPTION["preferred_family"] in (AF_INET, AF_INET6, AF_MPLS, AF_BRIDGE): family = family_name(OPTION["preferred_family"]) links = [link for link in links if "addr_info" in link and any(addr["family"] == family for addr in link["addr_info"])] - elif OPTION["preferred_family"] == AF_PACKET: - delete_keys(links, "addr_info") - ifconfig.dumps(links) + return EXIT_SUCCESS diff --git a/iproute4mac/iplink.py b/iproute4mac/iplink.py index dedaa9a..4fbaf69 100644 --- a/iproute4mac/iplink.py +++ b/iproute4mac/iplink.py @@ -1,5 +1,7 @@ +import iproute4mac.ifconfig as ifconfig + from iproute4mac.utils import * -from iproute4mac.ipaddress import ipaddr_list +from iproute4mac.ipaddress import get_ifconfig_links def usage(): @@ -78,6 +80,421 @@ def usage(): exit(-1) +# ip route { add | del | change | append | replace } ROUTE +# ROUTE := NODE_SPEC [ INFO_SPEC ] +# NODE_SPEC := [ TYPE ] PREFIX [ tos TOS ] +# [ table TABLE_ID ] [ proto RTPROTO ] +# [ scope SCOPE ] [ metric METRIC ] +# [ ttl-propagate { enabled | disabled } ] +# INFO_SPEC := { NH | nhid ID } OPTIONS FLAGS [ nexthop NH ]... +# NH := [ encap ENCAPTYPE ENCAPHDR ] [ via [ FAMILY ] ADDRESS ] +# [ dev STRING ] [ weight NUMBER ] NHFLAGS +# FAMILY := [ inet | inet6 | mpls | bridge | link ] +# OPTIONS := FLAGS [ mtu NUMBER ] [ advmss NUMBER ] [ as [ to ] ADDRESS ] +# [ rtt TIME ] [ rttvar TIME ] [ reordering NUMBER ] +# [ window NUMBER ] [ cwnd NUMBER ] [ initcwnd NUMBER ] +# [ ssthresh NUMBER ] [ realms REALM ] [ src ADDRESS ] +# [ rto_min TIME ] [ hoplimit NUMBER ] [ initrwnd NUMBER ] +# [ features FEATURES ] [ quickack BOOL ] [ congctl NAME ] +# [ pref PREF ] [ expires TIME ] [ fastopen_no_cookie BOOL ] +# TYPE := { unicast | local | broadcast | multicast | throw | +# unreachable | prohibit | blackhole | nat } +# TABLE_ID := [ local | main | default | all | NUMBER ] +# SCOPE := [ host | link | global | NUMBER ] +# NHFLAGS := [ onlink | pervasive ] +# RTPROTO := [ kernel | boot | static | NUMBER ] +# PREF := [ low | medium | high ] +# TIME := NUMBER[s|ms] +# BOOL := [1|0] +# FEATURES := ecn +# ENCAPTYPE := [ mpls | ip | ip6 | seg6 | seg6local | rpl | ioam6 ] +# ENCAPHDR := [ MPLSLABEL | SEG6HDR | SEG6LOCAL | IOAM6HDR ] +# SEG6HDR := [ mode SEGMODE ] segs ADDR1,ADDRi,ADDRn [hmac HMACKEYID] [cleanup] +# SEGMODE := [ encap | inline ] +# SEG6LOCAL := action ACTION [ OPTIONS ] [ count ] +# ACTION := { End | End.X | End.T | End.DX2 | End.DX6 | End.DX4 | +# End.DT6 | End.DT4 | End.DT46 | End.B6 | End.B6.Encaps | +# End.BM | End.S | End.AS | End.AM | End.BPF } +# OPTIONS := OPTION [ OPTIONS ] +# OPTION := { srh SEG6HDR | nh4 ADDR | nh6 ADDR | iif DEV | oif DEV | +# table TABLEID | vrftable TABLEID | endpoint PROGNAME } +# IOAM6HDR := trace prealloc type IOAM +def iplink_modify(argv): + name = None + dev = None + while argv: + opt = argv.pop(0) + # if strcmp(opt, "up"): + # req->i.ifi_change |= IFF_UP + # req->i.ifi_flags |= IFF_UP + # elif strcmp(opt, "down"): + # req->i.ifi_change |= IFF_UP + # req->i.ifi_flags &= ~IFF_UP + if strcmp(opt, "name"): + opt = next_arg(argv) + if name: + duparg("name", opt) + if not re.match(f"{IFNAME}$", opt): + invarg('"name" not a valid ifname', opt) + name = opt + if not dev: + dev = name + # elif strcmp(opt, "index"): + # opt = next_arg(argv) + # if (index) + # duparg("index", *argv) + # index = atoi(*argv) + # if (index <= 0) + # invarg("Invalid \"index\" value", *argv) + elif matches(opt, "link"): + link = next_arg(argv) + do_notimplemented([link]) + # elif matches(opt, "address"): + # opt = next_arg(argv) + # addr_len = ll_addr_a2n(abuf, sizeof(abuf), *argv) + # if (addr_len < 0) + # return -1 + # addattr_l(&req->n, sizeof(*req), + # IFLA_ADDRESS, abuf, addr_len) + # elif matches(opt, "broadcast") or strcmp(opt, "brd"): + # opt = next_arg(argv) + # len = ll_addr_a2n(abuf, sizeof(abuf), *argv) + # if (len < 0) + # return -1 + # addattr_l(&req->n, sizeof(*req), + # IFLA_BROADCAST, abuf, len) + # elif matches(opt, "txqueuelen", "txqlen") or strcmp(opt, "qlen"): + # opt = next_arg(argv) + # if (qlen != -1) + # duparg("txqueuelen", *argv) + # if (get_integer(&qlen, *argv, 0)) + # invarg("Invalid \"txqueuelen\" value\n", *argv) + # addattr_l(&req->n, sizeof(*req), + # IFLA_TXQLEN, &qlen, 4) + # elif strcmp(opt, "mtu"): + # opt = next_arg(argv) + # if (mtu != -1) + # duparg("mtu", *argv) + # if (get_integer(&mtu, *argv, 0)) + # invarg("Invalid \"mtu\" value\n", *argv) + # addattr_l(&req->n, sizeof(*req), IFLA_MTU, &mtu, 4) + # elif strcmp(opt, "xdpgeneric", "xdpdrv", "xdpoffload", "xdp"): + # bool generic = strcmp(opt, "xdpgeneric") == 0 + # bool drv = strcmp(opt, "xdpdrv") == 0 + # bool offload = strcmp(opt, "xdpoffload") == 0 + # + # opt = next_arg(argv) + # if (xdp_parse(&argc, &argv, req, dev, + # generic, drv, offload)) + # exit(-1) + # + # if (offload && name == dev) + # dev = NULL + # elif strcmp(opt, "netns"): + # opt = next_arg(argv) + # if (netns != -1) + # duparg("netns", *argv) + # netns = netns_get_fd(*argv) + # if (netns >= 0) + # addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_FD, + # &netns, 4) + # else if (get_integer(&netns, *argv, 0): + # addattr_l(&req->n, sizeof(*req), + # IFLA_NET_NS_PID, &netns, 4) + # else + # invarg("Invalid \"netns\" value\n", *argv) + # move_netns = true + # elif strcmp(opt, "multicast"): + # opt = next_arg(argv) + # req->i.ifi_change |= IFF_MULTICAST + # + # if strcmp(opt, "on"): + # req->i.ifi_flags |= IFF_MULTICAST + # else if strcmp(opt, "off"): + # req->i.ifi_flags &= ~IFF_MULTICAST + # else + # return on_off("multicast", *argv) + # elif strcmp(opt, "allmulticast"): + # opt = next_arg(argv) + # req->i.ifi_change |= IFF_ALLMULTI + # + # if strcmp(opt, "on"): + # req->i.ifi_flags |= IFF_ALLMULTI + # else if strcmp(opt, "off"): + # req->i.ifi_flags &= ~IFF_ALLMULTI + # else + # return on_off("allmulticast", *argv) + # elif strcmp(opt, "promisc"): + # opt = next_arg(argv) + # req->i.ifi_change |= IFF_PROMISC + # + # if strcmp(opt, "on"): + # req->i.ifi_flags |= IFF_PROMISC + # else if strcmp(opt, "off"): + # req->i.ifi_flags &= ~IFF_PROMISC + # else + # return on_off("promisc", *argv) + # elif strcmp(opt, "trailers"): + # opt = next_arg(argv) + # req->i.ifi_change |= IFF_NOTRAILERS + # + # if strcmp(opt, "off"): + # req->i.ifi_flags |= IFF_NOTRAILERS + # else if strcmp(opt, "on"): + # req->i.ifi_flags &= ~IFF_NOTRAILERS + # else + # return on_off("trailers", *argv) + # elif strcmp(opt, "arp"): + # opt = next_arg(argv) + # req->i.ifi_change |= IFF_NOARP + # + # if strcmp(opt, "on"): + # req->i.ifi_flags &= ~IFF_NOARP + # else if strcmp(opt, "off"): + # req->i.ifi_flags |= IFF_NOARP + # else + # return on_off("arp", *argv) + # elif strcmp(opt, "carrier"): + # int carrier + # + # opt = next_arg(argv) + # carrier = parse_on_off("carrier", *argv, &err) + # if (err) + # return err + # + # addattr8(&req->n, sizeof(*req), IFLA_CARRIER, carrier) + # elif strcmp(opt, "vf"): + # struct rtattr *vflist + # + # opt = next_arg(argv) + # if (get_integer(&vf, *argv, 0)) + # invarg("Invalid \"vf\" value\n", *argv) + # + # vflist = addattr_nest(&req->n, sizeof(*req), + # IFLA_VFINFO_LIST) + # if (!dev) + # missarg("dev") + # + # len = iplink_parse_vf(vf, &argc, &argv, req, dev) + # if (len < 0) + # return -1 + # addattr_nest_end(&req->n, vflist) + # + # if (name == dev) + # dev = NULL + # elif matches(opt, "master"): + # int ifindex + # + # opt = next_arg(argv) + # ifindex = ll_name_to_index(*argv) + # if (!ifindex) + # invarg("Device does not exist\n", *argv) + # addattr_l(&req->n, sizeof(*req), IFLA_MASTER, + # &ifindex, 4) + # elif strcmp(opt, "vrf"): + # int ifindex + # + # opt = next_arg(argv) + # ifindex = ll_name_to_index(*argv) + # if (!ifindex) + # invarg("Not a valid VRF name\n", *argv) + # if (!name_is_vrf(*argv)) + # invarg("Not a valid VRF name\n", *argv) + # addattr_l(&req->n, sizeof(*req), IFLA_MASTER, + # &ifindex, sizeof(ifindex)) + # elif matches(opt, "nomaster"): + # int ifindex = 0 + # + # addattr_l(&req->n, sizeof(*req), IFLA_MASTER, + # &ifindex, 4) + # elif matches(opt, "dynamic"): + # opt = next_arg(argv) + # req->i.ifi_change |= IFF_DYNAMIC + # + # if strcmp(opt, "on"): + # req->i.ifi_flags |= IFF_DYNAMIC + # else if strcmp(opt, "off"): + # req->i.ifi_flags &= ~IFF_DYNAMIC + # else + # return on_off("dynamic", *argv) + # elif matches(opt, "type"): + # opt = next_arg(argv) + # *type = *argv + # argc--; argv++ + # break + # elif matches(opt, "alias"): + # opt = next_arg(argv) + # len = strlen(*argv) + # if (len >= IFALIASZ) + # invarg("alias too long\n", *argv) + # addattr_l(&req->n, sizeof(*req), IFLA_IFALIAS, + # *argv, len) + # elif strcmp(opt, "group"): + # opt = next_arg(argv) + # if (group != -1) + # duparg("group", *argv) + # if (rtnl_group_a2n(&group, *argv)) + # invarg("Invalid \"group\" value\n", *argv) + # addattr32(&req->n, sizeof(*req), IFLA_GROUP, group) + # elif strcmp(opt, "mode"): + # int mode + # + # opt = next_arg(argv) + # mode = get_link_mode(*argv) + # if (mode < 0) + # invarg("Invalid link mode\n", *argv) + # addattr8(&req->n, sizeof(*req), IFLA_LINKMODE, mode) + # elif strcmp(opt, "state"): + # int state + # + # opt = next_arg(argv) + # state = get_operstate(*argv) + # if (state < 0) + # invarg("Invalid operstate\n", *argv) + # + # addattr8(&req->n, sizeof(*req), IFLA_OPERSTATE, state) + # elif matches(opt, "numtxqueues"): + # opt = next_arg(argv) + # if (numtxqueues != -1) + # duparg("numtxqueues", *argv) + # if (get_integer(&numtxqueues, *argv, 0)) + # invarg("Invalid \"numtxqueues\" value\n", + # *argv) + # addattr_l(&req->n, sizeof(*req), IFLA_NUM_TX_QUEUES, + # &numtxqueues, 4) + # elif matches(opt, "numrxqueues"): + # opt = next_arg(argv) + # if (numrxqueues != -1) + # duparg("numrxqueues", *argv) + # if (get_integer(&numrxqueues, *argv, 0)) + # invarg("Invalid \"numrxqueues\" value\n", + # *argv) + # addattr_l(&req->n, sizeof(*req), IFLA_NUM_RX_QUEUES, + # &numrxqueues, 4) + # elif matches(opt, "addrgenmode"): + # struct rtattr *afs, *afs6 + # int mode + # + # opt = next_arg(argv) + # mode = get_addr_gen_mode(*argv) + # if (mode < 0) + # invarg("Invalid address generation mode\n", + # *argv) + # afs = addattr_nest(&req->n, sizeof(*req), IFLA_AF_SPEC) + # afs6 = addattr_nest(&req->n, sizeof(*req), AF_INET6) + # addattr8(&req->n, sizeof(*req), + # IFLA_INET6_ADDR_GEN_MODE, mode) + # addattr_nest_end(&req->n, afs6) + # addattr_nest_end(&req->n, afs) + # elif matches(opt, "link-netns"): + # opt = next_arg(argv) + # if (link_netnsid != -1) + # duparg("link-netns/link-netnsid", *argv) + # link_netnsid = get_netnsid_from_name(*argv) + # /* No nsid? Try to assign one. */ + # if (link_netnsid < 0) + # set_netnsid_from_name(*argv, -1) + # link_netnsid = get_netnsid_from_name(*argv) + # if (link_netnsid < 0) + # invarg("Invalid \"link-netns\" value\n", + # *argv) + # addattr32(&req->n, sizeof(*req), IFLA_LINK_NETNSID, + # link_netnsid) + # elif matches(opt, "link-netnsid"): + # opt = next_arg(argv) + # if (link_netnsid != -1) + # duparg("link-netns/link-netnsid", *argv) + # if (get_integer(&link_netnsid, *argv, 0)) + # invarg("Invalid \"link-netnsid\" value\n", + # *argv) + # addattr32(&req->n, sizeof(*req), IFLA_LINK_NETNSID, + # link_netnsid) + # elif strcmp(opt, "protodown"): + # unsigned int proto_down + # + # opt = next_arg(argv) + # proto_down = parse_on_off("protodown", *argv, &err) + # if (err) + # return err + # addattr8(&req->n, sizeof(*req), IFLA_PROTO_DOWN, + # proto_down) + # elif strcmp(opt, "protodown_reason"): + # struct rtattr *pr + # __u32 preason = 0, prvalue = 0, prmask = 0 + # + # opt = next_arg(argv) + # if (protodown_reason_a2n(&preason, *argv)) + # invarg("invalid protodown reason\n", *argv) + # opt = next_arg(argv) + # prmask = 1 << preason + # if matches(opt, "on"): + # prvalue |= prmask + # else if matches(opt, "off"): + # prvalue &= ~prmask + # else + # return on_off("protodown_reason", *argv) + # pr = addattr_nest(&req->n, sizeof(*req), + # IFLA_PROTO_DOWN_REASON | NLA_F_NESTED) + # addattr32(&req->n, sizeof(*req), + # IFLA_PROTO_DOWN_REASON_MASK, prmask) + # addattr32(&req->n, sizeof(*req), + # IFLA_PROTO_DOWN_REASON_VALUE, prvalue) + # addattr_nest_end(&req->n, pr) + # elif strcmp(opt, "gso_max_size"): + # unsigned int max_size + # + # opt = next_arg(argv) + # if (get_unsigned(&max_size, *argv, 0)) + # invarg("Invalid \"gso_max_size\" value\n", + # *argv) + # addattr32(&req->n, sizeof(*req), + # IFLA_GSO_MAX_SIZE, max_size) + # elif strcmp(opt, "gso_max_segs"): + # unsigned int max_segs + # + # opt = next_arg(argv) + # if (get_unsigned(&max_segs, *argv, 0) || + # max_segs > GSO_MAX_SEGS) + # invarg("Invalid \"gso_max_segs\" value\n", + # *argv) + # addattr32(&req->n, sizeof(*req), + # IFLA_GSO_MAX_SEGS, max_segs) + # elif strcmp(opt, "gro_max_size"): + # unsigned int max_size + # + # opt = next_arg(argv) + # if (get_unsigned(&max_size, *argv, 0)) + # invarg("Invalid \"gro_max_size\" value\n", + # *argv) + # addattr32(&req->n, sizeof(*req), + # IFLA_GRO_MAX_SIZE, max_size) + # elif strcmp(opt, "parentdev"): + # opt = next_arg(argv) + # addattr_l(&req->n, sizeof(*req), IFLA_PARENT_DEV_NAME, + # *argv, strlen(*argv) + 1) + else: + if matches(opt, "help"): + usage() + + if strcmp(opt, "dev"): + opt = next_arg(argv) + if dev != name: + duparg2("dev", opt) + if not re.match(f"{IFNAME}$", opt): + invarg('"dev" not a valid ifname', opt) + dev = opt + + # Allow "ip link add dev" and "ip link add name" + if not name: + name = dev + elif not dev: + dev = name + elif name != dev: + name = dev + + return EXIT_SUCCESS + + # ip link show [ DEVICE | group GROUP ] [ up ] [ master DEVICE ] [ type ETYPE ] [ vrf NAME ] # TYPE := [ bridge | bond | can | dummy | hsr | ifb | ipoib | macvlan | macvtap # | vcan | vxcan | veth | vlan | vxlan | ip6tnl | ipip | sit | gre @@ -86,8 +503,11 @@ def usage(): # | netdevsim | rmnet | xfrm ] # ETYPE := [ TYPE | bridge_slave | bond_slave ] def iplink_list(argv): - OPTION["preferred_family"] = AF_PACKET - return ipaddr_list(argv, usage) + links = get_ifconfig_links(argv, usage) + delete_keys(links, "addr_info") + ifconfig.dumps(links) + + return EXIT_SUCCESS def do_iplink(argv): @@ -95,7 +515,7 @@ def do_iplink(argv): return iplink_list(argv) cmd = argv.pop(0) - if matches(cmd, "add", "change", "set", "replace", "delete"): + if matches(cmd, "add", "set", "change", "replace", "delete"): return iplink_modify(argv) elif matches(cmd, "show", "lst", "list"): return iplink_list(argv) diff --git a/iproute4mac/iproute.py b/iproute4mac/iproute.py index d4e4b61..541ad0e 100644 --- a/iproute4mac/iproute.py +++ b/iproute4mac/iproute.py @@ -279,12 +279,7 @@ def iproute_modify(cmd, argv): tid = next_arg(argv) do_notimplemented([tid]) elif strcmp(opt, "dev", "oif"): - dev = next_arg(argv) - # try: - # lookup for dev in ifconfig - # except: - # invarg('Cannot find device "{dev}"') - entry["dev"] = dev + entry["dev"] = next_arg(argv) elif matches(opt, "pref"): pref = next_arg(argv) try: diff --git a/iproute4mac/netstat.py b/iproute4mac/netstat.py index 4bfed00..513b441 100644 --- a/iproute4mac/netstat.py +++ b/iproute4mac/netstat.py @@ -79,64 +79,58 @@ def parse(res): match = netstatRegEx(line) if match.route: - route = match.route.groupdict() - debug(f"Found route {route}") + debug(f"Found route: {line.strip()}") + dst, prefix, gateway, flags, dev, expire = match.route.groups() - if route["flags"] == RTF_WASCLONED or route["flags"] == RTF_PROXY: - debug("Skip cloned/proxy rotue") + if RTF_WASCLONED in flags: + debug("Skip cloned rotue") continue + if RTF_PROXY in flags: + debug("Skip proxy rotue") + continue + + if prefix: + dst = f"{dst}/{prefix}" + dst = Prefix(dst) - if re.search(LLADDR, route["gateway"]): + scope = None + if re.match(LLADDR, gateway): if not OPTION["show_details"]: debug("Skip host rotue") continue - del route["gateway"] - - if re.match(IPV4ADDR, route["dst"]) or ("gateway" in route and re.match(IPV4ADDR, route["gateway"])): - family = AF_INET + gateway = None + elif gateway.startswith("link#"): + scope = "link" + gateway = None else: - family = AF_INET6 - if family == AF_INET and OPTION["preferred_family"] == AF_INET6: - debug("Skip IPv4 rotue") - continue - if family == AF_INET6 and OPTION["preferred_family"] == AF_INET: - debug("Skip IPv6 rotue") - continue + gateway = Prefix(gateway) + if dst.family == AF_UNSPEC: + dst.family = gateway.family - if route["dst"] != "default" and family == AF_INET: - dots = route["dst"].count(".") - if dots < 3: - route["dst"] = route["dst"] + ".0" * (3 - dots) - if not route["prefix"]: - route["prefix"] = 8 * (dots + 1) - if route["prefix"]: - route["dst"] = f"{route["dst"]}/{route["prefix"]}" + if OPTION["preferred_family"] != AF_UNSPEC and dst.family != OPTION["preferred_family"]: + debug("Skip unmatched IP version rotue") + continue # protocol - if RTF_STATIC in route["flags"]: + if RTF_STATIC in flags: protocol = "static" - elif any(flag in route["flags"] for flag in (RTF_DYNAMIC, RTF_MODIFIED)): + elif any(flag in flags for flag in (RTF_DYNAMIC, RTF_MODIFIED)): protocol = "redirect" else: protocol = "kernel" # scope - if RTF_HOST in route["flags"]: + if RTF_HOST in flags: scope = "host" - if "gateway" in route and route["gateway"].startswith("link#"): - scope = "link" - del route["gateway"] elif OPTION["show_details"]: scope = "global" - else: - scope = None # address type - if RTF_BLACKHOLE in route["flags"]: + if RTF_BLACKHOLE in flags: addr_type = "blackhole" - elif RTF_BROADCAST in route["flags"]: + elif RTF_BROADCAST in flags: addr_type = "broadcast" - elif RTF_MULTICAST in route["flags"]: + elif RTF_MULTICAST in flags: addr_type = "multicast" elif OPTION["show_details"]: addr_type = "unicast" @@ -145,12 +139,12 @@ def parse(res): route = { "type": addr_type, - "dst": route["dst"], - "gateway": route["gateway"] if "gateway" in route else None, - "dev": route["dev"], + "dst": dst, + "gateway": gateway, + "dev": dev, "protocol": protocol, "scope": scope, - "expire": int(route["expire"]) if route["expire"] and route["expire"] != "!" else None, + "expire": int(expire) if expire and expire.isdigit() else None, "flags": [], } routes.append({k: v for k, v in route.items() if v is not None}) diff --git a/iproute4mac/route.py b/iproute4mac/route.py index d12d8a8..2cbe3e8 100644 --- a/iproute4mac/route.py +++ b/iproute4mac/route.py @@ -108,9 +108,9 @@ def parse(res): match = routeGetRegEx(line) if match.dst: - route["dst"] = match.dst.group("dst") + route["dst"] = Prefix(match.dst.group("dst")) elif match.gateway: - route["gateway"] = match.gateway.group("gateway") + route["gateway"] = Prefix(match.gateway.group("gateway")) elif match.dev: route["dev"] = match.dev.group("dev") elif match.flags: diff --git a/iproute4mac/utils.py b/iproute4mac/utils.py index 68a824e..b7cd779 100644 --- a/iproute4mac/utils.py +++ b/iproute4mac/utils.py @@ -10,11 +10,12 @@ """ Costants """ -LOG_ERROR = 0 -LOG_WARN = 1 -LOG_INFO = 2 -LOG_DEBUG = 3 -LOG_LABEL = ("Error", "Warning", "Info", "Debug") +LOG_STDERR = 0 +LOG_ERROR = 1 +LOG_WARN = 2 +LOG_INFO = 3 +LOG_DEBUG = 4 +LOG_LABEL = (None, "Error", "Warning", "Info", "Debug") # socket.h AF_UNSPEC = 0 @@ -71,6 +72,9 @@ ND6_INFINITE_LIFETIME = 0xFFFFFFFF +IFNAME = r"(?:\w+\d+)" + + """ Global options """ OPTION = { "preferred_family": AF_UNSPEC, @@ -91,7 +95,7 @@ "do_all": False, "uid": os.getuid(), "compress_vlans": False, - "verbose": 0, + "verbose": 1, } @@ -99,12 +103,14 @@ def stdout(*argv, sep="", end=""): print(*argv, sep=sep, end=end) -def stderr(text, log_level=LOG_ERROR): +def stderr(text, log_level=LOG_STDERR): if OPTION["verbose"] < log_level: return if text[-1] != "\n": text += "\n" - sys.stderr.write(LOG_LABEL[log_level] + ": " + text) + if log_level > LOG_STDERR: + text = LOG_LABEL[log_level] + ": " + text + sys.stderr.write(text) def error(text): @@ -132,6 +138,14 @@ def invarg(msg, arg): error(f'argument "{arg}" is wrong: {msg}') +def duparg(key, arg): + error(f'duplicate "{key}": "{arg}" is the second value.') + + +def duparg2(key, arg): + error(f'either "{key}" is duplicate, or "{arg}" is a garbage.') + + def incomplete_command(): stderr('Command line is not complete. Try option "help"') exit(-1) @@ -321,43 +335,56 @@ def __init__(self, prefix): if prefix == "default": self._prefix = None self._any = False - elif prefix == "any": + return + if prefix == "any": self._prefix = None self._any = True - elif "/" in prefix: - self._prefix = ipaddress.ip_network(prefix) - self._any = False + return + self._any = False + if "/" in prefix: + prefix, prefixlen = prefix.split("/") + else: + prefixlen = None + if ":" not in prefix and (dots := prefix.count(".")) < 3: + prefix += ".0" * (3 - dots) + if not prefixlen: + prefixlen = str((dots + 1) * 8) + if prefixlen: + self._prefix = ipaddress.ip_network(f"{prefix}/{prefixlen}") else: self._prefix = ipaddress.ip_address(prefix) - self._any = False def __eq__(self, other): - if self.family != other.family: - return False - if isinstance(type(self._prefix), type(other.prefix)): - return self._prefix == other.prefix - if self.is_host and other.is_host: - return self.address == other.address + if self._prefix and other._prefix: + return self.prefixlen == other.prefixlen and self.address == other.address + if self._is_default and other.is_default: + return True + if other._is_default and self.is_default: + return True return False def __contains__(self, other): - if isinstance(self._prefix, ipaddress.IPv4Network | ipaddress.IPv6Network): + if self._is_network: if other.is_host: return other.address in self._prefix else: - return other.prefix.subnet_of(self._prefix) - elif other.is_host: + return other._prefix.subnet_of(self._prefix) + if other.is_host: return other.address == self._prefix + if self._is_default and other.is_default: + return True + if other._is_default and self.is_default: + return True return False - def __repr__(self): + def __str__(self): if self.is_default: return "default" if self.is_any: return "any" return str(self._prefix) - def __str__(self): + def __repr__(self): if self._prefix: return str(self._prefix) if self.is_any: