summaryrefslogtreecommitdiffstats
path: root/ip/ip.c
diff options
context:
space:
mode:
Diffstat (limited to 'ip/ip.c')
-rw-r--r--ip/ip.c327
1 files changed, 327 insertions, 0 deletions
diff --git a/ip/ip.c b/ip/ip.c
new file mode 100644
index 0000000..e51fa20
--- /dev/null
+++ b/ip/ip.c
@@ -0,0 +1,327 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * ip.c "ip" utility frontend.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <errno.h>
+
+#include "version.h"
+#include "utils.h"
+#include "ip_common.h"
+#include "namespace.h"
+#include "color.h"
+#include "rt_names.h"
+#include "bpf_util.h"
+
+#ifndef LIBDIR
+#define LIBDIR "/usr/lib"
+#endif
+
+int preferred_family = AF_UNSPEC;
+int human_readable;
+int use_iec;
+int show_stats;
+int show_details;
+int oneline;
+int brief;
+int json;
+int timestamp;
+int echo_request;
+int force;
+int max_flush_loops = 10;
+int batch_mode;
+bool do_all;
+
+struct rtnl_handle rth = { .fd = -1 };
+
+const char *get_ip_lib_dir(void)
+{
+ const char *lib_dir;
+
+ lib_dir = getenv("IP_LIB_DIR");
+ if (!lib_dir)
+ lib_dir = LIBDIR "/ip";
+
+ return lib_dir;
+}
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+ fprintf(stderr,
+ "Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }\n"
+ " ip [ -force ] -batch filename\n"
+ "where OBJECT := { address | addrlabel | fou | help | ila | ioam | l2tp | link |\n"
+ " macsec | maddress | monitor | mptcp | mroute | mrule |\n"
+ " neighbor | neighbour | netconf | netns | nexthop | ntable |\n"
+ " ntbl | route | rule | sr | stats | tap | tcpmetrics |\n"
+ " token | tunnel | tuntap | vrf | xfrm }\n"
+ " OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n"
+ " -h[uman-readable] | -iec | -j[son] | -p[retty] |\n"
+ " -f[amily] { inet | inet6 | mpls | bridge | link } |\n"
+ " -4 | -6 | -M | -B | -0 |\n"
+ " -l[oops] { maximum-addr-flush-attempts } | -echo | -br[ief] |\n"
+ " -o[neline] | -t[imestamp] | -ts[hort] | -b[atch] [filename] |\n"
+ " -rc[vbuf] [size] | -n[etns] name | -N[umeric] | -a[ll] |\n"
+ " -c[olor]}\n");
+ exit(-1);
+}
+
+static int do_help(int argc, char **argv)
+{
+ usage();
+ return 0;
+}
+
+static const struct cmd {
+ const char *cmd;
+ int (*func)(int argc, char **argv);
+} cmds[] = {
+ { "address", do_ipaddr },
+ { "addrlabel", do_ipaddrlabel },
+ { "maddress", do_multiaddr },
+ { "route", do_iproute },
+ { "rule", do_iprule },
+ { "neighbor", do_ipneigh },
+ { "neighbour", do_ipneigh },
+ { "ntable", do_ipntable },
+ { "ntbl", do_ipntable },
+ { "link", do_iplink },
+ { "l2tp", do_ipl2tp },
+ { "fou", do_ipfou },
+ { "ila", do_ipila },
+ { "macsec", do_ipmacsec },
+ { "tunnel", do_iptunnel },
+ { "tunl", do_iptunnel },
+ { "tuntap", do_iptuntap },
+ { "tap", do_iptuntap },
+ { "token", do_iptoken },
+ { "tcpmetrics", do_tcp_metrics },
+ { "tcp_metrics", do_tcp_metrics },
+ { "monitor", do_ipmonitor },
+ { "xfrm", do_xfrm },
+ { "mroute", do_multiroute },
+ { "mrule", do_multirule },
+ { "netns", do_netns },
+ { "netconf", do_ipnetconf },
+ { "vrf", do_ipvrf},
+ { "sr", do_seg6 },
+ { "nexthop", do_ipnh },
+ { "mptcp", do_mptcp },
+ { "ioam", do_ioam6 },
+ { "help", do_help },
+ { "stats", do_ipstats },
+ { 0 }
+};
+
+static int do_cmd(const char *argv0, int argc, char **argv, bool final)
+{
+ const struct cmd *c;
+
+ for (c = cmds; c->cmd; ++c) {
+ if (matches(argv0, c->cmd) == 0)
+ return -(c->func(argc-1, argv+1));
+ }
+
+ if (final)
+ fprintf(stderr, "Object \"%s\" is unknown, try \"ip help\".\n", argv0);
+ return EXIT_FAILURE;
+}
+
+static int ip_batch_cmd(int argc, char *argv[], void *data)
+{
+ const int *orig_family = data;
+
+ preferred_family = *orig_family;
+ return do_cmd(argv[0], argc, argv, true);
+}
+
+static int batch(const char *name)
+{
+ int orig_family = preferred_family;
+ int ret;
+
+ if (rtnl_open(&rth, 0) < 0) {
+ fprintf(stderr, "Cannot open rtnetlink\n");
+ return EXIT_FAILURE;
+ }
+
+ batch_mode = 1;
+ ret = do_batch(name, force, ip_batch_cmd, &orig_family);
+
+ rtnl_close(&rth);
+ return ret;
+}
+
+int main(int argc, char **argv)
+{
+ const char *libbpf_version;
+ char *batch_file = NULL;
+ char *basename;
+ int color = CONF_COLOR;
+
+ /* to run vrf exec without root, capabilities might be set, drop them
+ * if not needed as the first thing.
+ * execv will drop them for the child command.
+ * vrf exec requires:
+ * - cap_dac_override to create the cgroup subdir in /sys
+ * - cap_bpf to load the BPF program
+ * - cap_net_admin to set the socket into the cgroup
+ */
+ if (argc < 3 || strcmp(argv[1], "vrf") != 0 ||
+ strcmp(argv[2], "exec") != 0)
+ drop_cap();
+
+ basename = strrchr(argv[0], '/');
+ if (basename == NULL)
+ basename = argv[0];
+ else
+ basename++;
+
+ while (argc > 1) {
+ char *opt = argv[1];
+
+ if (strcmp(opt, "--") == 0) {
+ argc--; argv++;
+ break;
+ }
+ if (opt[0] != '-')
+ break;
+ if (opt[1] == '-')
+ opt++;
+ if (matches(opt, "-loops") == 0) {
+ argc--;
+ argv++;
+ if (argc <= 1)
+ usage();
+ max_flush_loops = atoi(argv[1]);
+ } else if (matches(opt, "-family") == 0) {
+ argc--;
+ argv++;
+ if (argc <= 1)
+ usage();
+ if (strcmp(argv[1], "help") == 0)
+ usage();
+ else
+ preferred_family = read_family(argv[1]);
+ if (preferred_family == AF_UNSPEC)
+ invarg("invalid protocol family", argv[1]);
+ } else if (strcmp(opt, "-4") == 0) {
+ preferred_family = AF_INET;
+ } else if (strcmp(opt, "-6") == 0) {
+ preferred_family = AF_INET6;
+ } else if (strcmp(opt, "-0") == 0) {
+ preferred_family = AF_PACKET;
+ } else if (strcmp(opt, "-M") == 0) {
+ preferred_family = AF_MPLS;
+ } else if (strcmp(opt, "-B") == 0) {
+ preferred_family = AF_BRIDGE;
+ } else if (matches(opt, "-human") == 0 ||
+ matches(opt, "-human-readable") == 0) {
+ ++human_readable;
+ } else if (matches(opt, "-iec") == 0) {
+ ++use_iec;
+ } else if (matches(opt, "-stats") == 0 ||
+ matches(opt, "-statistics") == 0) {
+ ++show_stats;
+ } else if (matches(opt, "-details") == 0) {
+ ++show_details;
+ } else if (matches(opt, "-resolve") == 0) {
+ ++resolve_hosts;
+ } else if (matches(opt, "-oneline") == 0) {
+ ++oneline;
+ } else if (matches(opt, "-timestamp") == 0) {
+ ++timestamp;
+ } else if (matches(opt, "-tshort") == 0) {
+ ++timestamp;
+ ++timestamp_short;
+ } else if (matches(opt, "-Version") == 0) {
+ printf("ip utility, iproute2-%s", version);
+ libbpf_version = get_libbpf_version();
+ if (libbpf_version)
+ printf(", libbpf %s", libbpf_version);
+ printf("\n");
+ exit(0);
+ } else if (matches(opt, "-force") == 0) {
+ ++force;
+ } else if (matches(opt, "-batch") == 0) {
+ argc--;
+ argv++;
+ if (argc <= 1)
+ usage();
+ batch_file = argv[1];
+ } else if (matches(opt, "-brief") == 0) {
+ ++brief;
+ } else if (matches(opt, "-json") == 0) {
+ ++json;
+ } else if (matches(opt, "-pretty") == 0) {
+ ++pretty;
+ } else if (matches(opt, "-rcvbuf") == 0) {
+ unsigned int size;
+
+ argc--;
+ argv++;
+ if (argc <= 1)
+ usage();
+ if (get_unsigned(&size, argv[1], 0)) {
+ fprintf(stderr, "Invalid rcvbuf size '%s'\n",
+ argv[1]);
+ exit(-1);
+ }
+ rcvbuf = size;
+ } else if (matches_color(opt, &color)) {
+ } else if (matches(opt, "-help") == 0) {
+ usage();
+ } else if (matches(opt, "-netns") == 0) {
+ NEXT_ARG();
+ if (netns_switch(argv[1]))
+ exit(-1);
+ } else if (matches(opt, "-Numeric") == 0) {
+ ++numeric;
+ } else if (matches(opt, "-all") == 0) {
+ do_all = true;
+ } else if (strcmp(opt, "-echo") == 0) {
+ ++echo_request;
+ } else {
+ fprintf(stderr,
+ "Option \"%s\" is unknown, try \"ip -help\".\n",
+ opt);
+ exit(-1);
+ }
+ argc--; argv++;
+ }
+
+ _SL_ = oneline ? "\\" : "\n";
+
+ check_enable_color(color, json);
+
+ if (batch_file)
+ return batch(batch_file);
+
+ if (rtnl_open(&rth, 0) < 0)
+ exit(1);
+
+ rtnl_set_strict_dump(&rth);
+
+ if (strlen(basename) > 2) {
+ int ret = do_cmd(basename+2, argc, argv, false);
+ if (ret != EXIT_FAILURE)
+ return ret;
+ }
+
+ if (argc > 1)
+ return do_cmd(argv[1], argc-1, argv+1, true);
+
+ rtnl_close(&rth);
+ usage();
+}