source: ogAgent-Git/macos/scripts/ip.py @ 64089e0

decorare-oglive-methodsfixes-winlgromero-filebeatmainmodulesnew-browserno-ptt-paramogadmcliogadmclient-statusogagent-jobsogcore1oglogoglog2override-moduleping1ping2ping3ping4py3-winreport-progresstlsunification2unification3versions
Last change on this file since 64089e0 was dc7c6af, checked in by Natalia Serrano <natalia.serrano@…>, 10 months ago

refs #474 fix regex to match ipv6 addresses

The former regex failed to match an IPv6 that didn't include "::", which are
obviously valid. Change the regex so they are caught.

  • Property mode set to 100755
File size: 23.4 KB
Line 
1#!/usr/bin/env python3
2# encoding: utf8
3
4"""
5  This program is taken from https://github.com/brona/iproute2mac
6  When doing 'brew install iproute2mac', we get an old version (1.4.2) that doesn't support 'ip -json'
7  The alternative installation method recomended in the project's README file is 'curl; chmod; mv'
8  Therefore we make the decision of shipping this ip.py (version 1.5.0) along opengnsys, which is pretty much the same as curling it
9"""
10
11
12"""
13  iproute2mac
14  CLI wrapper for basic network utilites on Mac OS X.
15  Homepage: https://github.com/brona/iproute2mac
16
17  The MIT License (MIT)
18  Copyright (c) 2015 Bronislav Robenek <brona@robenek.me>
19"""
20
21import ipaddress
22import json
23import os
24import random
25import re
26import socket
27import string
28import subprocess
29import sys
30import types
31
32# Version
33VERSION = "1.5.0"
34
35# Utilities
36SUDO = "/usr/bin/sudo"
37IFCONFIG = "/sbin/ifconfig"
38ROUTE = "/sbin/route"
39NETSTAT = "/usr/sbin/netstat"
40NDP = "/usr/sbin/ndp"
41ARP = "/usr/sbin/arp"
42NETWORKSETUP = "/usr/sbin/networksetup"
43
44
45# Helper functions
46def perror(*args):
47    sys.stderr.write(*args)
48    sys.stderr.write("\n")
49
50
51def execute_cmd(cmd):
52    print("Executing: %s" % cmd)
53    status, output = subprocess.getstatusoutput(cmd)
54    if status == 0:  # unix/linux commands 0 true, 1 false
55        print(output)
56        return True
57    else:
58        perror(output)
59        return False
60
61
62def json_dump(data, pretty):
63    if pretty:
64        print(json.dumps(data, indent=4))
65    else:
66        print(json.dumps(data, separators=(",", ":")))
67    return True
68
69# Classful to CIDR conversion with "default" being passed through
70def cidr_from_netstat_dst(target):
71    if target == "default":
72        return target
73
74    dots = target.count(".")
75    if target.find("/") == -1:
76        addr = target
77        netmask = (dots + 1) * 8
78    else:
79        [addr, netmask] = target.split("/")
80
81    addr = addr + ".0" * (3 - dots)
82    return addr + "/" + str(netmask)
83
84
85# Convert hexadecimal netmask in prefix length
86def netmask_to_length(mask):
87    return int(mask, 16).bit_count()
88
89
90def any_startswith(words, test):
91    for word in words:
92        if word.startswith(test):
93            return True
94    return False
95
96
97# Handles passsing return value, error messages and program exit on error
98def help_msg(help_func):
99    def wrapper(func):
100        def inner(*args, **kwargs):
101            if not func(*args, **kwargs):
102                specific = eval(help_func)
103                if specific:
104                    if isinstance(specific, types.FunctionType):
105                        if args and kwargs:
106                            specific(*args, **kwargs)
107                        else:
108                            specific()
109                        return False
110                    else:
111                        raise Exception("Function expected for: " + help_func)
112                else:
113                    raise Exception(
114                        "Function variant not defined: " + help_func
115                    )
116            return True
117
118        return inner
119
120    return wrapper
121
122
123# Generate random MAC address with XenSource Inc. OUI
124# http://www.linux-kvm.com/sites/default/files/macgen.py
125def randomMAC():
126    mac = [
127        0x00,
128        0x16,
129        0x3E,
130        random.randint(0x00, 0x7F),
131        random.randint(0x00, 0xFF),
132        random.randint(0x00, 0xFF),
133    ]
134    return ":".join(["%02x" % x for x in mac])
135
136
137# Decode ifconfig output
138def parse_ifconfig(res, af, address):
139    links = []
140    count = 1
141
142    for r in res.split("\n"):
143        if re.match(r"^\w+:", r):
144            if count > 1:
145                links.append(link)
146            (ifname, flags, mtu, ifindex) = re.findall(r"^(\w+): flags=\d+<(.*)> mtu (\d+) index (\d+)", r)[0]
147            flags = flags.split(",")
148            link = {
149                "ifindex": int(ifindex),
150                "ifname": ifname,
151                "flags": flags,
152                "mtu": int(mtu),
153                "operstate": "UNKNOWN",
154                "link_type": "unknown"
155            }
156            if "LOOPBACK" in flags:
157                link["link_type"] = "loopback"
158                link["address"] = "00:00:00:00:00:00"
159                link["broadcast"] = "00:00:00:00:00:00"
160            elif "POINTOPOINT" in flags:
161                link["link_type"] = "none"
162            count = count + 1
163        else:
164            if re.match(r"^\s+ether ", r):
165                link["link_type"] = "ether"
166                link["address"] = re.findall(r"(\w\w:\w\w:\w\w:\w\w:\w\w:\w\w)", r)[0]
167                link["broadcast"] = "ff:ff:ff:ff:ff:ff"
168            elif address and re.match(r"^\s+inet ", r) and af != 6:
169                (local, netmask) = re.findall(r"inet (\d+\.\d+\.\d+\.\d+) netmask (0x[0-9a-f]+)", r)[0]
170                addr = {
171                    "family": "inet",
172                    "local": local,
173                    "prefixlen": netmask_to_length(netmask),
174                }
175                if re.match(r"^.*broadcast", r):
176                    addr["broadcast"] = re.findall(r"broadcast (\d+\.\d+\.\d+\.\d+)", r)[0]
177                link["addr_info"] = link.get("addr_info", []) + [addr]
178            elif address and re.match(r"^\s+inet6 ", r) and af != 4:
179                (local, prefixlen) = re.findall(r"inet6 ((?:[a-f0-9:]+:+)+[a-f0-9]+)%*\w* +prefixlen (\d+)", r)[0]
180                link["addr_info"] = link.get("addr_info", []) + [{
181                  "family": "inet6",
182                  "local": local,
183                  "prefixlen": int(prefixlen)
184                }]
185            elif re.match(r"^\s+status: ", r):
186                match re.findall(r"status: (\w+)", r)[0]:
187                    case "active":
188                        link["operstate"] = "UP"
189                    case "inactive":
190                        link["operstate"] = "DOWN"
191
192    if count > 1:
193        links.append(link)
194
195    return links
196
197
198def link_addr_show(argv, af, json_print, pretty_json, address):
199    if len(argv) > 0 and argv[0] == "dev":
200        argv.pop(0)
201    if len(argv) > 0:
202        param = argv[0]
203    else:
204        param = "-a"
205
206    status, res = subprocess.getstatusoutput(
207        IFCONFIG + " -v " + param + " 2>/dev/null"
208    )
209    if status:  # unix status
210        if res == "":
211            perror(param + " not found")
212        else:
213            perror(res)
214        return False
215
216    links = parse_ifconfig(res, af, address)
217
218    if json_print:
219        return json_dump(links, pretty_json)
220
221    for l in links:
222        print("%d: %s: <%s> mtu %d status %s" % (
223            l["ifindex"], l["ifname"], ",".join(l["flags"]), l["mtu"], l["operstate"]
224        ))
225        print(
226            "    link/" + l["link_type"] +
227            ((" " + l["address"]) if "address" in l else "") +
228            ((" brd " + l["broadcast"]) if "broadcast" in l else "")
229        )
230        for a in l.get("addr_info", []):
231            print(
232                "    %s %s/%d" % (a["family"], a["local"], a["prefixlen"]) +
233                ((" brd " + a["broadcast"]) if "broadcast" in a else "")
234            )
235
236    return True
237
238
239# Help
240def do_help(argv=None, af=None, json_print=None, pretty_json=None):
241    perror("Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }")
242    perror("where  OBJECT := { link | addr | route | neigh }")
243    perror("       OPTIONS := { -V[ersion] | -j[son] | -p[retty] |")
244    perror("                    -4 | -6 }")
245    perror("iproute2mac")
246    perror("Homepage: https://github.com/brona/iproute2mac")
247    perror(
248        "This is CLI wrapper for basic network utilities on Mac OS X"
249        " inspired with iproute2 on Linux systems."
250    )
251    perror(
252        "Provided functionality is limited and command output is not"
253        " fully compatible with iproute2."
254    )
255    perror(
256        "For advanced usage use netstat, ifconfig, ndp, arp, route "
257        " and networksetup directly."
258    )
259    exit(255)
260
261
262def do_help_route():
263    perror("Usage: ip route list")
264    perror("       ip route get ADDRESS")
265    perror("       ip route { add | del | replace } ROUTE")
266    perror("       ip route flush cache")
267    perror("       ip route flush table main")
268    perror("ROUTE := NODE_SPEC [ INFO_SPEC ]")
269    perror("NODE_SPEC := [ TYPE ] PREFIX")
270    perror("INFO_SPEC := NH")
271    perror("TYPE := { blackhole }")
272    perror("NH := { via ADDRESS | gw ADDRESS | nexthop ADDRESS | dev STRING }")
273    exit(255)
274
275
276def do_help_addr():
277    perror("Usage: ip addr show [ dev STRING ]")
278    perror("       ip addr { add | del } PREFIX dev STRING")
279    exit(255)
280
281
282def do_help_link():
283    perror("Usage: ip link show [ DEVICE ]")
284    perror("       ip link set dev DEVICE")
285    perror("                [ { up | down } ]")
286    perror("                [ address { LLADDR | factory | random } ]")
287    perror("                [ mtu MTU ]")
288    exit(255)
289
290
291def do_help_neigh():
292    perror("Usage: ip neighbour show [ [ to ] PREFIX ] [ dev DEV ]")
293    perror("       ip neighbour flush [ dev DEV ]")
294    exit(255)
295
296
297# Route Module
298@help_msg("do_help_route")
299def do_route(argv, af, json_print, pretty_json):
300    if not argv or (
301        any_startswith(["show", "lst", "list"], argv[0]) and len(argv) == 1
302    ):
303        return do_route_list(af, json_print, pretty_json)
304    elif "get".startswith(argv[0]) and len(argv) == 2:
305        argv.pop(0)
306        return do_route_get(argv, af, json_print, pretty_json)
307    elif "add".startswith(argv[0]) and len(argv) >= 3:
308        argv.pop(0)
309        return do_route_add(argv, af)
310    elif "delete".startswith(argv[0]) and len(argv) >= 2:
311        argv.pop(0)
312        return do_route_del(argv, af)
313    elif "replace".startswith(argv[0]) and len(argv) >= 3:
314        argv.pop(0)
315        return do_route_del(argv, af) and do_route_add(argv, af)
316    elif "flush".startswith(argv[0]) and len(argv) >= 1:
317        argv.pop(0)
318        return do_route_flush(argv, af)
319    else:
320        return False
321    return True
322
323
324def do_route_list(af, json_print, pretty_json):
325    # ip route prints IPv6 or IPv4, never both
326    inet = "inet6" if af == 6 else "inet"
327    status, res = subprocess.getstatusoutput(
328        NETSTAT + " -nr -f " + inet + " 2>/dev/null"
329    )
330    if status:
331        perror(res)
332        return False
333    res = res.split("\n")
334    res = res[4:]  # Removes first 4 lines
335
336    routes = []
337
338    for r in res:
339        ra = r.split()
340        target = ra[0]
341        gw = ra[1]
342        flags = ra[2]
343        # macOS Mojave and earlier vs Catalina
344        dev = ra[5] if len(ra) >= 6 else ra[3]
345        if flags.find("W") != -1:
346            continue
347        if af == 6:
348            target = re.sub(r"%[^ ]+/", "/", target)
349        else:
350            target = cidr_from_netstat_dst(target)
351        if flags.find("B") != -1:
352            routes.append({"type": "blackhole", "dst": target, "flags": []})
353            continue
354        if re.match(r"link.+", gw):
355            routes.append({"dst": target, "dev": dev, "scope": "link", "flags": []})
356        else:
357            routes.append({"dst": target, "gateway": gw, "dev": dev, "flags": []})
358
359    if json_print:
360        return json_dump(routes, pretty_json)
361
362    for route in routes:
363        if "type" in route:
364            print("%s %s" % (route["type"], route["dst"]))
365        elif "scope" in route:
366            print("%s dev %s scope %s" % (route["dst"], route["dev"], route["scope"]))
367        elif "gateway" in route:
368            print("%s via %s dev %s" % (route["dst"], route["gateway"], route["dev"]))
369
370    return True
371
372
373def do_route_add(argv, af):
374    options = ""
375    if argv[0] == "blackhole":
376        argv.pop(0)
377        if len(argv) != 1:
378            return False
379        argv.append("via")
380        argv.append("::1" if ":" in argv[0] or af == 6 else "127.0.0.1")
381        options = "-blackhole"
382
383    if len(argv) not in (3, 5):
384        return False
385
386    if len(argv) == 5:
387        perror(
388            "iproute2mac: Ignoring last 2 arguments, not implemented: {} {}".format(
389                argv[3], argv[4]
390            )
391        )
392
393    if argv[1] in ["via", "nexthop", "gw"]:
394        gw = argv[2]
395    elif argv[1] in ["dev"]:
396        gw = "-interface " + argv[2]
397    else:
398        do_help_route()
399
400    prefix = argv[0]
401    inet = "-inet6 " if ":" in prefix or af == 6 else ""
402
403    return execute_cmd(
404        SUDO + " " + ROUTE + " add " + inet + prefix + " " + gw + " " + options
405    )
406
407
408def do_route_del(argv, af):
409    options = ""
410    if argv[0] == "blackhole":
411        argv.pop(0)
412        if len(argv) != 1:
413            return False
414        if ":" in argv[0] or af == 6:
415            options = " ::1 -blackhole"
416        else:
417            options = " 127.0.0.1 -blackhole"
418
419    prefix = argv[0]
420    inet = "-inet6 " if ":" in prefix or af == 6 else ""
421    return execute_cmd(
422        SUDO + " " + ROUTE + " delete " + inet + prefix + options
423    )
424
425
426def do_route_flush(argv, af):
427    if not argv:
428        perror('"ip route flush" requires arguments.')
429        perror("")
430        return False
431
432    # https://github.com/brona/iproute2mac/issues/38
433    # http://linux-ip.net/html/tools-ip-route.html
434    if argv[0] == "cache":
435        print("iproute2mac: There is no route cache to flush in MacOS,")
436        print("             returning 0 status code for compatibility.")
437        return True
438    elif len(argv) == 2 and argv[0] == "table" and argv[1] == "main":
439        family = "-inet6" if af == 6 else "-inet"
440        print("iproute2mac: Flushing all routes")
441        return execute_cmd(SUDO + " " + ROUTE + " -n flush " + family)
442    else:
443        return False
444
445
446def do_route_get(argv, af, json_print, pretty_json):
447    target = argv[0]
448
449    inet = ""
450    if ":" in target or af == 6:
451        inet = "-inet6 "
452        family = socket.AF_INET6
453    else:
454        family = socket.AF_INET
455
456    status, res = subprocess.getstatusoutput(
457        ROUTE + " -n get " + inet + target
458    )
459    if status:  # unix status or not in table
460        perror(res)
461        return False
462    if res.find("not in table") >= 0:
463        perror(res)
464        exit(1)
465
466    res = dict(
467        re.findall(
468            r"^\W*((?:route to|destination|gateway|interface)): (.+)$",
469            res,
470            re.MULTILINE,
471        )
472    )
473
474    route = {"dst": res["route to"], "dev": res["interface"]}
475
476    if "gateway" in res:
477        route["gateway"] = res["gateway"]
478
479    try:
480        s = socket.socket(family, socket.SOCK_DGRAM)
481        s.connect((route["dst"], 7))
482        route["prefsrc"] = src_ip = s.getsockname()[0]
483        s.close()
484    except:
485        pass
486
487    route["flags"] = []
488    route["uid"] = os.getuid()
489    route["cache"] = []
490
491    if json_print:
492        return json_dump([route], pretty_json)
493
494    print(
495        route["dst"] +
496        ((" via " + route["gateway"]) if "gateway" in route else "") +
497        " dev " + route["dev"] +
498        ((" src " + route["prefsrc"]) if "prefsrc" in route else "") +
499        " uid " + str(route["uid"])
500    )
501
502    return True
503
504
505# Addr Module
506@help_msg("do_help_addr")
507def do_addr(argv, af, json_print, pretty_json):
508    if not argv:
509        argv.append("show")
510
511    if any_startswith(["show", "lst", "list"], argv[0]):
512        argv.pop(0)
513        return do_addr_show(argv, af, json_print, pretty_json)
514    elif "add".startswith(argv[0]) and len(argv) >= 3:
515        argv.pop(0)
516        return do_addr_add(argv, af)
517    elif "delete".startswith(argv[0]) and len(argv) >= 3:
518        argv.pop(0)
519        return do_addr_del(argv, af)
520    else:
521        return False
522    return True
523
524
525def do_addr_show(argv, af, json_print, pretty_json):
526    return link_addr_show(argv, af, json_print, pretty_json, True)
527
528
529def do_addr_add(argv, af):
530    if len(argv) < 2:
531        return False
532
533    dst = ""
534    if argv[1] == "peer":
535        argv.pop(1)
536        dst = argv.pop(1)
537
538    if argv[1] == "dev":
539        argv.pop(1)
540    else:
541        return False
542    try:
543        addr = argv[0]
544        dev = argv[1]
545    except IndexError:
546        perror("dev not found")
547        exit(1)
548    inet = ""
549    if ":" in addr or af == 6:
550        af = 6
551        inet = " inet6"
552    return execute_cmd(
553        SUDO + " " + IFCONFIG + " " + dev + inet + " add " + addr + " " + dst
554    )
555
556
557def do_addr_del(argv, af):
558    if len(argv) < 2:
559        return False
560    if argv[1] == "dev":
561        argv.pop(1)
562    try:
563        addr = argv[0]
564        dev = argv[1]
565    except IndexError:
566        perror("dev not found")
567        exit(1)
568    inet = "inet"
569    if ":" in addr or af == 6:
570        af = 6
571        inet = "inet6"
572    return execute_cmd(
573        SUDO + " " + IFCONFIG + " " + dev + " " + inet + " " + addr + " remove"
574    )
575
576
577# Link module
578@help_msg("do_help_link")
579def do_link(argv, af, json_print, pretty_json):
580    if not argv:
581        argv.append("show")
582
583    if any_startswith(["show", "lst", "list"], argv[0]):
584        argv.pop(0)
585        return do_link_show(argv, af, json_print, pretty_json)
586    elif "set".startswith(argv[0]):
587        argv.pop(0)
588        return do_link_set(argv, af)
589    else:
590        return False
591    return True
592
593
594def do_link_show(argv, af, json_print, pretty_json):
595    return link_addr_show(argv, af, json_print, pretty_json, False)
596
597
598def do_link_set(argv, af):
599    if not argv:
600        return False
601    elif argv[0] == "dev":
602        argv.pop(0)
603
604    if len(argv) < 2:
605        return False
606
607    dev = argv[0]
608
609    IFCONFIG_DEV_CMD = SUDO + " " + IFCONFIG + " " + dev
610    try:
611        args = iter(argv)
612        for arg in args:
613            if arg == "up":
614                if not execute_cmd(IFCONFIG_DEV_CMD + " up"):
615                    return False
616            elif arg == "down":
617                if not execute_cmd(IFCONFIG_DEV_CMD + " down"):
618                    return False
619            elif arg in ["address", "addr", "lladdr"]:
620                addr = next(args)
621                if addr in ["random", "rand"]:
622                    addr = randomMAC()
623                elif addr == "factory":
624                    (status, res) = subprocess.getstatusoutput(
625                        NETWORKSETUP + " -listallhardwareports"
626                    )
627                    if status != 0:
628                        return False
629                    details = re.findall(
630                        r"^(?:Device|Ethernet Address): (.+)$",
631                        res,
632                        re.MULTILINE,
633                    )
634                    addr = details[details.index(dev) + 1]
635                if not execute_cmd(IFCONFIG_DEV_CMD + " lladdr " + addr):
636                    return False
637            elif arg == "mtu":
638                mtu = int(next(args))
639                if not execute_cmd(IFCONFIG_DEV_CMD + " mtu " + str(mtu)):
640                    return False
641    except Exception:
642        return False
643    return True
644
645
646# Neigh module
647@help_msg("do_help_neigh")
648def do_neigh(argv, af, json_print, pretty_json):
649    if not argv:
650        argv.append("show")
651
652    if any_startswith(["show", "list", "lst"], argv[0]) and len(argv) <= 5:
653        argv.pop(0)
654        return do_neigh_show(argv, af, json_print, pretty_json)
655    elif "flush".startswith(argv[0]):
656        argv.pop(0)
657        return do_neigh_flush(argv, af)
658    else:
659        return False
660
661
662def do_neigh_show(argv, af, json_print, pretty_json):
663    prefix = None
664    dev = None
665    try:
666        while argv:
667            arg = argv.pop(0)
668            if arg == "to":
669                prefix = argv.pop(0)
670            elif arg == "dev":
671                dev = argv.pop(0)
672            elif prefix is None:
673                prefix = arg
674            else:
675                return False
676        if prefix:
677            prefix = ipaddress.ip_network(prefix, strict=False)
678    except Exception:
679        return False
680
681    nd_ll_states = {
682        "R": "REACHABLE",
683        "S": "STALE",
684        "D": "DELAY",
685        "P": "PROBE",
686        "I": "INCOMPLETE",
687        "N": "INCOMPLETE",
688        "W": "INCOMPLETE",
689    }
690
691    neighs = []
692
693    if af != 4:
694        res = subprocess.run(
695            [NDP, "-an"], capture_output=True, text=True, check=True
696        )
697        for row in res.stdout.splitlines()[1:]:
698            cols = row.split()
699            entry = {"dst": re.sub(r"%.+$", "", cols[0])}
700            if cols[1] != "(incomplete)":
701                entry["lladdr"] = cols[1]
702            entry["dev"] = cols[2]
703            if dev and entry["dev"] != dev:
704                continue
705            if prefix and ipaddress.ip_address(entry["dst"]) not in prefix:
706                continue
707            if cols[1] == "(incomplete)" and cols[4] != "R":
708                entry["status"] = ["INCOMPLETE"]
709            else:
710                entry["status"] = [nd_ll_states[cols[4]]]
711            entry["router"] = len(cols) >= 6 and cols[5] == "R"
712            neighs.append(entry)
713
714    if af != 6:
715        args = [ARP, "-anl"]
716        if dev:
717            args += ["-i", dev]
718
719        res = subprocess.run(args, capture_output=True, text=True, check=True)
720        for row in res.stdout.splitlines()[1:]:
721            cols = row.split()
722            entry = {"dst": cols[0]}
723            if cols[1] != "(incomplete)":
724                entry["lladdr"] = cols[1]
725            entry["dev"] = cols[4]
726            if dev and entry["dev"] != dev:
727                continue
728            if prefix and ipaddress.ip_address(entry["dst"]) not in prefix:
729                continue
730            if cols[1] == "(incomplete)":
731                entry["status"] = ["INCOMPLETE"]
732            else:
733                entry["status"] = ["REACHABLE"]
734            entry["router"] = False
735            neighs.append(entry)
736
737    if json_print:
738        return json_dump(neighs, pretty_json)
739
740    for nb in neighs:
741        print(
742            nb["dst"] +
743            ("", " dev " + nb["dev"], "")[dev == None] +
744            ("", " router")[nb["router"]] +
745            " %s" % (nb["status"][0])
746        )
747
748    return True
749
750
751def do_neigh_flush(argv, af):
752    if len(argv) != 2:
753        perror("Flush requires arguments.")
754        exit(1)
755
756    if argv[0] != "dev":
757        return False
758    dev = argv[1]
759
760    if af != 4:
761        print(
762            "iproute2mac: NDP doesn't support filtering by interface,"
763            "flushing all IPv6 entries."
764        )
765        execute_cmd(SUDO + " " + NDP + " -cn")
766    if af != 6:
767        execute_cmd(SUDO + " " + ARP + " -a -d -i " + dev)
768    return True
769
770
771# Match iproute2 commands
772# https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/ip.c#n86
773cmds = [
774    ("address", do_addr),
775    ("route", do_route),
776    ("neighbor", do_neigh),
777    ("neighbour", do_neigh),
778    ("link", do_link),
779    ("help", do_help),
780]
781
782
783@help_msg("do_help")
784def main(argv):
785    af = -1  # default / both
786    json_print = False
787    pretty_json = False
788
789    while argv and argv[0].startswith("-"):
790        # Turn --opt into -opt
791        argv[0] = argv[0][1:] if argv[0][1] == "-" else argv[0]
792        # Process options
793        if argv[0] == "-6":
794            af = 6
795            argv.pop(0)
796        elif argv[0] == "-4":
797            af = 4
798            argv.pop(0)
799        elif argv[0].startswith("-color"):
800            perror("iproute2mac: Color option is not implemented")
801            argv.pop(0)
802        elif "-json".startswith(argv[0]):
803            json_print = True
804            argv.pop(0)
805        elif "-pretty".startswith(argv[0]):
806            pretty_json = True
807            argv.pop(0)
808        elif "-Version".startswith(argv[0]):
809            print("iproute2mac, v" + VERSION)
810            exit(0)
811        elif "-help".startswith(argv[0]):
812            return False
813        else:
814            perror('Option "{}" is unknown, try "ip help".'.format(argv[0]))
815            exit(255)
816
817    if not argv:
818        return False
819
820    for cmd, cmd_func in cmds:
821        if cmd.startswith(argv[0]):
822            argv.pop(0)
823            # Functions return true or terminate with exit(255)
824            # See help_msg and do_help*
825            return cmd_func(argv, af, json_print, pretty_json)
826
827    perror('Object "{}" is unknown, try "ip help".'.format(argv[0]))
828    exit(1)
829
830
831if __name__ == "__main__":
832    main(sys.argv[1:])
Note: See TracBrowser for help on using the repository browser.