github.hscsec.cn/u-root/u-root@v7.0.0+incompatible/cmds/core/ip/ip.go (about)

     1  // Copyright 2012-2017 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // ip manipulates network addresses, interfaces, routing, and other config.
     6  package main
     7  
     8  import (
     9  	"errors"
    10  	"fmt"
    11  	l "log"
    12  	"net"
    13  	"os"
    14  	"strings"
    15  
    16  	flag "github.com/spf13/pflag"
    17  
    18  	"github.com/vishvananda/netlink"
    19  )
    20  
    21  var inet6 = flag.BoolP("6", "6", false, "use ipv6")
    22  
    23  // The language implemented by the standard 'ip' is not super consistent
    24  // and has lots of convenience shortcuts.
    25  // The BNF the standard ip  shows you doesn't show many of these short cuts, and
    26  // it is wrong in other ways.
    27  // For this ip command:.
    28  // The inputs is just the set of args.
    29  // The input is very short -- it's not a program!
    30  // Each token is just a string and we need not produce terminals with them -- they can
    31  // just be the terminals and we can switch on them.
    32  // The cursor is always our current token pointer. We do a simple recursive descent parser
    33  // and accumulate information into a global set of variables. At any point we can see into the
    34  // whole set of args and see where we are. We can indicate at each point what we're expecting so
    35  // that in usage() or recover() we can tell the user exactly what we wanted, unlike the standard ip,
    36  // which just dumps a whole (incorrect) BNF at you when you do anything wrong.
    37  // To handle errors in too few arguments, we just do a recover block. That lets us blindly
    38  // reference the arg[] array without having to check the length everywhere.
    39  
    40  // RE: the use of globals. The reason is simple: we parse one command, do it, and quit.
    41  // It doesn't make sense to write this otherwise.
    42  var (
    43  	// Cursor is out next token pointer.
    44  	// The language of this command doesn't require much more.
    45  	cursor    int
    46  	arg       []string
    47  	whatIWant []string
    48  	log       = l.New(os.Stdout, "", 0)
    49  
    50  	addrScopes = map[netlink.Scope]string{
    51  		netlink.SCOPE_UNIVERSE: "global",
    52  		netlink.SCOPE_HOST:     "host",
    53  		netlink.SCOPE_SITE:     "site",
    54  		netlink.SCOPE_LINK:     "link",
    55  		netlink.SCOPE_NOWHERE:  "nowhere",
    56  	}
    57  )
    58  
    59  // the pattern:
    60  // at each level parse off arg[0]. If it matches, continue. If it does not, all error with how far you got, what arg you saw,
    61  // and why it did not work out.
    62  
    63  func usage() error {
    64  	return fmt.Errorf("this was fine: '%v', and this was left, '%v', and this was not understood, '%v'; only options are '%v'",
    65  		arg[0:cursor], arg[cursor:], arg[cursor], whatIWant)
    66  }
    67  
    68  func one(cmd string, cmds []string) string {
    69  	var x, n int
    70  	for i, v := range cmds {
    71  		if strings.HasPrefix(v, cmd) {
    72  			n++
    73  			x = i
    74  		}
    75  	}
    76  	if n == 1 {
    77  		return cmds[x]
    78  	}
    79  	return ""
    80  }
    81  
    82  // in the ip command, turns out 'dev' is a noise word.
    83  // The BNF it shows is not right in that case.
    84  // Always make 'dev' optional.
    85  func dev() (netlink.Link, error) {
    86  	cursor++
    87  	whatIWant = []string{"dev", "device name"}
    88  	if arg[cursor] == "dev" {
    89  		cursor++
    90  	}
    91  	whatIWant = []string{"device name"}
    92  	return netlink.LinkByName(arg[cursor])
    93  }
    94  
    95  func maybename() (string, error) {
    96  	cursor++
    97  	whatIWant = []string{"name", "device name"}
    98  	if arg[cursor] == "name" {
    99  		cursor++
   100  	}
   101  	whatIWant = []string{"device name"}
   102  	return arg[cursor], nil
   103  }
   104  
   105  func addrip() error {
   106  	var err error
   107  	var addr *netlink.Addr
   108  	if len(arg) == 1 {
   109  		return showLinks(os.Stdout, true)
   110  	}
   111  	cursor++
   112  	whatIWant = []string{"add", "del"}
   113  	cmd := arg[cursor]
   114  
   115  	c := one(cmd, whatIWant)
   116  	switch c {
   117  	case "add", "del":
   118  		cursor++
   119  		whatIWant = []string{"CIDR format address"}
   120  		addr, err = netlink.ParseAddr(arg[cursor])
   121  		if err != nil {
   122  			return err
   123  		}
   124  	default:
   125  		return usage()
   126  	}
   127  	iface, err := dev()
   128  	if err != nil {
   129  		return err
   130  	}
   131  	switch c {
   132  	case "add":
   133  		if err := netlink.AddrAdd(iface, addr); err != nil {
   134  			return fmt.Errorf("adding %v to %v failed: %v", arg[1], arg[2], err)
   135  		}
   136  	case "del":
   137  		if err := netlink.AddrDel(iface, addr); err != nil {
   138  			return fmt.Errorf("deleting %v from %v failed: %v", arg[1], arg[2], err)
   139  		}
   140  	default:
   141  		return fmt.Errorf("devip: arg[0] changed: can't happen")
   142  	}
   143  	return nil
   144  }
   145  
   146  func neigh() error {
   147  	if len(arg) != 1 {
   148  		return errors.New("neigh subcommands not supported yet")
   149  	}
   150  	return showNeighbours(os.Stdout, true)
   151  }
   152  
   153  func linkshow() error {
   154  	cursor++
   155  	whatIWant = []string{"<nothing>", "<device name>"}
   156  	if len(arg[cursor:]) == 0 {
   157  		return showLinks(os.Stdout, false)
   158  	}
   159  	return nil
   160  }
   161  
   162  func setHardwareAddress(iface netlink.Link) error {
   163  	cursor++
   164  	hwAddr, err := net.ParseMAC(arg[cursor])
   165  	if err != nil {
   166  		return fmt.Errorf("%v cant parse mac addr %v: %v", iface.Attrs().Name, hwAddr, err)
   167  	}
   168  	err = netlink.LinkSetHardwareAddr(iface, hwAddr)
   169  	if err != nil {
   170  		return fmt.Errorf("%v cant set mac addr %v: %v", iface.Attrs().Name, hwAddr, err)
   171  	}
   172  	return nil
   173  }
   174  
   175  func linkset() error {
   176  	iface, err := dev()
   177  	if err != nil {
   178  		return err
   179  	}
   180  
   181  	cursor++
   182  	whatIWant = []string{"address", "up", "down", "master"}
   183  	switch one(arg[cursor], whatIWant) {
   184  	case "address":
   185  		return setHardwareAddress(iface)
   186  	case "up":
   187  		if err := netlink.LinkSetUp(iface); err != nil {
   188  			return fmt.Errorf("%v can't make it up: %v", iface.Attrs().Name, err)
   189  		}
   190  	case "down":
   191  		if err := netlink.LinkSetDown(iface); err != nil {
   192  			return fmt.Errorf("%v can't make it down: %v", iface.Attrs().Name, err)
   193  		}
   194  	case "master":
   195  		cursor++
   196  		whatIWant = []string{"device name"}
   197  		master, err := netlink.LinkByName(arg[cursor])
   198  		if err != nil {
   199  			return err
   200  		}
   201  		return netlink.LinkSetMaster(iface, master)
   202  	default:
   203  		return usage()
   204  	}
   205  	return nil
   206  }
   207  
   208  func linkadd() error {
   209  	name, err := maybename()
   210  	if err != nil {
   211  		return err
   212  	}
   213  	attrs := netlink.LinkAttrs{Name: name}
   214  
   215  	cursor++
   216  	whatIWant = []string{"type"}
   217  	if arg[cursor] != "type" {
   218  		return usage()
   219  	}
   220  
   221  	cursor++
   222  	whatIWant = []string{"bridge"}
   223  	if arg[cursor] != "bridge" {
   224  		return usage()
   225  	}
   226  	return netlink.LinkAdd(&netlink.Bridge{LinkAttrs: attrs})
   227  }
   228  
   229  func link() error {
   230  	if len(arg) == 1 {
   231  		return linkshow()
   232  	}
   233  
   234  	cursor++
   235  	whatIWant = []string{"show", "set", "add"}
   236  	cmd := arg[cursor]
   237  
   238  	switch one(cmd, whatIWant) {
   239  	case "show":
   240  		return linkshow()
   241  	case "set":
   242  		return linkset()
   243  	case "add":
   244  		return linkadd()
   245  	}
   246  	return usage()
   247  }
   248  
   249  func routeshow() error {
   250  	return showRoutes(*inet6)
   251  }
   252  
   253  func nodespec() string {
   254  	cursor++
   255  	whatIWant = []string{"default", "CIDR"}
   256  	return arg[cursor]
   257  }
   258  
   259  func nexthop() (string, *netlink.Addr, error) {
   260  	cursor++
   261  	whatIWant = []string{"via"}
   262  	if arg[cursor] != "via" {
   263  		return "", nil, usage()
   264  	}
   265  	nh := arg[cursor]
   266  	cursor++
   267  	whatIWant = []string{"Gateway CIDR"}
   268  	addr, err := netlink.ParseAddr(arg[cursor])
   269  	if err != nil {
   270  		return "", nil, fmt.Errorf("failed to parse gateway CIDR: %v", err)
   271  	}
   272  	return nh, addr, nil
   273  }
   274  
   275  func routeadddefault() error {
   276  	nh, nhval, err := nexthop()
   277  	if err != nil {
   278  		return err
   279  	}
   280  	// TODO: NHFLAGS.
   281  	l, err := dev()
   282  	if err != nil {
   283  		return err
   284  	}
   285  	switch nh {
   286  	case "via":
   287  		log.Printf("Add default route %v via %v", nhval, l.Attrs().Name)
   288  		r := &netlink.Route{LinkIndex: l.Attrs().Index, Gw: nhval.IPNet.IP}
   289  		if err := netlink.RouteAdd(r); err != nil {
   290  			return fmt.Errorf("error adding default route to %v: %v", l.Attrs().Name, err)
   291  		}
   292  		return nil
   293  	}
   294  	return usage()
   295  }
   296  
   297  func routeadd() error {
   298  	ns := nodespec()
   299  	switch ns {
   300  	case "default":
   301  		return routeadddefault()
   302  	default:
   303  		addr, err := netlink.ParseAddr(arg[cursor])
   304  		if err != nil {
   305  			return usage()
   306  		}
   307  		d, err := dev()
   308  		if err != nil {
   309  			return usage()
   310  		}
   311  		r := &netlink.Route{LinkIndex: d.Attrs().Index, Dst: addr.IPNet}
   312  		if err := netlink.RouteAdd(r); err != nil {
   313  			return fmt.Errorf("error adding route %s -> %s: %v", addr, d.Attrs().Name, err)
   314  		}
   315  		return nil
   316  	}
   317  }
   318  
   319  func route() error {
   320  	cursor++
   321  	if len(arg[cursor:]) == 0 {
   322  		return routeshow()
   323  	}
   324  
   325  	whatIWant = []string{"show", "add"}
   326  	switch one(arg[cursor], whatIWant) {
   327  	case "show":
   328  		return routeshow()
   329  	case "add":
   330  		return routeadd()
   331  	}
   332  	return usage()
   333  }
   334  
   335  func main() {
   336  	// When this is embedded in busybox we need to reinit some things.
   337  	whatIWant = []string{"addr", "route", "link", "neigh"}
   338  	cursor = 0
   339  	flag.Parse()
   340  	arg = flag.Args()
   341  
   342  	defer func() {
   343  		switch err := recover().(type) {
   344  		case nil:
   345  		case error:
   346  			if strings.Contains(err.Error(), "index out of range") {
   347  				log.Fatalf("Args: %v, I got to arg %v, I wanted %v after that", arg, cursor, whatIWant)
   348  			} else if strings.Contains(err.Error(), "slice bounds out of range") {
   349  				log.Fatalf("Args: %v, I got to arg %v, I wanted %v after that", arg, cursor, whatIWant)
   350  			}
   351  			log.Fatalf("Bummer: %v", err)
   352  		default:
   353  			log.Fatalf("unexpected panic value: %T(%v)", err, err)
   354  		}
   355  	}()
   356  
   357  	// The ip command doesn't actually follow the BNF it prints on error.
   358  	// There are lots of handy shortcuts that people will expect.
   359  	var err error
   360  	switch one(arg[cursor], whatIWant) {
   361  	case "addr":
   362  		err = addrip()
   363  	case "link":
   364  		err = link()
   365  	case "route":
   366  		err = route()
   367  	case "neigh":
   368  		err = neigh()
   369  	default:
   370  		usage()
   371  	}
   372  	if err != nil {
   373  		log.Fatal(err)
   374  	}
   375  
   376  }