iproute2/ip/ipfou.c
<<
>>
Prefs
   1/*
   2 * ipfou.c      FOU (foo over UDP) support
   3 *
   4 *              This program is free software; you can redistribute it and/or
   5 *              modify it under the terms of the GNU General Public License
   6 *              as published by the Free Software Foundation; either version
   7 *              2 of the License, or (at your option) any later version.
   8 *
   9 * Authors:     Tom Herbert <therbert@google.com>
  10 */
  11
  12#include <netdb.h>
  13#include <stdio.h>
  14#include <stdlib.h>
  15#include <string.h>
  16#include <net/if.h>
  17#include <linux/fou.h>
  18#include <linux/genetlink.h>
  19#include <linux/ip.h>
  20#include <arpa/inet.h>
  21
  22#include "libgenl.h"
  23#include "utils.h"
  24#include "ip_common.h"
  25#include "json_print.h"
  26
  27static void usage(void)
  28{
  29        fprintf(stderr,
  30                "Usage: ip fou add port PORT { ipproto PROTO  | gue }\n"
  31                "                  [ local IFADDR ] [ peer IFADDR ]\n"
  32                "                  [ peer_port PORT ] [ dev IFNAME ]\n"
  33                "       ip fou del port PORT [ local IFADDR ]\n"
  34                "                  [ peer IFADDR ] [ peer_port PORT ]\n"
  35                "                  [ dev IFNAME ]\n"
  36                "       ip fou show\n"
  37                "\n"
  38                "Where: PROTO { ipproto-name | 1..255 }\n"
  39                "       PORT { 1..65535 }\n"
  40                "       IFADDR { addr }\n");
  41
  42        exit(-1);
  43}
  44
  45/* netlink socket */
  46static struct rtnl_handle genl_rth = { .fd = -1 };
  47static int genl_family = -1;
  48
  49#define FOU_REQUEST(_req, _bufsiz, _cmd, _flags)        \
  50        GENL_REQUEST(_req, _bufsiz, genl_family, 0,     \
  51                     FOU_GENL_VERSION, _cmd, _flags)
  52
  53static int fou_parse_opt(int argc, char **argv, struct nlmsghdr *n,
  54                         bool adding)
  55{
  56        const char *local = NULL, *peer = NULL;
  57        __u16 port, peer_port = 0;
  58        __u8 family = preferred_family;
  59        bool gue_set = false;
  60        int ipproto_set = 0;
  61        __u8 ipproto, type;
  62        int port_set = 0;
  63        int index = 0;
  64
  65        if (preferred_family == AF_UNSPEC) {
  66                family = AF_INET;
  67        }
  68
  69        while (argc > 0) {
  70                if (!matches(*argv, "port")) {
  71                        NEXT_ARG();
  72
  73                        if (get_be16(&port, *argv, 0) || port == 0)
  74                                invarg("invalid port", *argv);
  75                        port_set = 1;
  76                } else if (!matches(*argv, "ipproto")) {
  77                        struct protoent *servptr;
  78
  79                        NEXT_ARG();
  80
  81                        servptr = getprotobyname(*argv);
  82                        if (servptr)
  83                                ipproto = servptr->p_proto;
  84                        else if (get_u8(&ipproto, *argv, 0) || ipproto == 0)
  85                                invarg("invalid ipproto", *argv);
  86                        ipproto_set = 1;
  87                } else if (!matches(*argv, "gue")) {
  88                        gue_set = true;
  89                } else if (!matches(*argv, "-6")) {
  90                        family = AF_INET6;
  91                } else if (!matches(*argv, "local")) {
  92                        NEXT_ARG();
  93
  94                        local = *argv;
  95                } else if (!matches(*argv, "peer")) {
  96                        NEXT_ARG();
  97
  98                        peer = *argv;
  99                } else if (!matches(*argv, "peer_port")) {
 100                        NEXT_ARG();
 101
 102                        if (get_be16(&peer_port, *argv, 0) || peer_port == 0)
 103                                invarg("invalid peer port", *argv);
 104                } else if (!matches(*argv, "dev")) {
 105                        const char *ifname;
 106
 107                        NEXT_ARG();
 108
 109                        ifname = *argv;
 110
 111                        if (check_ifname(ifname)) {
 112                                fprintf(stderr, "fou: invalid device name\n");
 113                                exit(EXIT_FAILURE);
 114                        }
 115
 116                        index = ll_name_to_index(ifname);
 117
 118                        if (!index) {
 119                                fprintf(stderr, "fou: unknown device name\n");
 120                                exit(EXIT_FAILURE);
 121                        }
 122                } else {
 123                        fprintf(stderr
 124                                , "fou: unknown command \"%s\"?\n", *argv);
 125                        usage();
 126                        return -1;
 127                }
 128                argc--, argv++;
 129        }
 130
 131        if (!port_set) {
 132                fprintf(stderr, "fou: missing port\n");
 133                return -1;
 134        }
 135
 136        if (!ipproto_set && !gue_set && adding) {
 137                fprintf(stderr, "fou: must set ipproto or gue\n");
 138                return -1;
 139        }
 140
 141        if (ipproto_set && gue_set) {
 142                fprintf(stderr, "fou: cannot set ipproto and gue\n");
 143                return -1;
 144        }
 145
 146        if ((peer_port && !peer) || (peer && !peer_port)) {
 147                fprintf(stderr, "fou: both peer and peer port must be set\n");
 148                return -1;
 149        }
 150
 151        type = gue_set ? FOU_ENCAP_GUE : FOU_ENCAP_DIRECT;
 152
 153        addattr16(n, 1024, FOU_ATTR_PORT, port);
 154        addattr8(n, 1024, FOU_ATTR_TYPE, type);
 155        addattr8(n, 1024, FOU_ATTR_AF, family);
 156
 157        if (ipproto_set)
 158                addattr8(n, 1024, FOU_ATTR_IPPROTO, ipproto);
 159
 160        if (local) {
 161                inet_prefix local_addr;
 162                __u8 attr_type = family == AF_INET ? FOU_ATTR_LOCAL_V4 :
 163                                                     FOU_ATTR_LOCAL_V6;
 164
 165                if (get_addr(&local_addr, local, family)) {
 166                        fprintf(stderr, "fou: parsing local address failed\n");
 167                        exit(EXIT_FAILURE);
 168                }
 169                addattr_l(n, 1024, attr_type, &local_addr.data,
 170                          local_addr.bytelen);
 171        }
 172
 173        if (peer) {
 174                inet_prefix peer_addr;
 175                __u8 attr_type = family == AF_INET ? FOU_ATTR_PEER_V4 :
 176                                                     FOU_ATTR_PEER_V6;
 177
 178                if (get_addr(&peer_addr, peer, family)) {
 179                        fprintf(stderr, "fou: parsing peer address failed\n");
 180                        exit(EXIT_FAILURE);
 181                }
 182                addattr_l(n, 1024, attr_type, &peer_addr.data,
 183                          peer_addr.bytelen);
 184
 185                if (peer_port)
 186                        addattr16(n, 1024, FOU_ATTR_PEER_PORT, peer_port);
 187        }
 188
 189        if (index)
 190                addattr32(n, 1024, FOU_ATTR_IFINDEX, index);
 191
 192        return 0;
 193}
 194
 195static int do_add(int argc, char **argv)
 196{
 197        FOU_REQUEST(req, 1024, FOU_CMD_ADD, NLM_F_REQUEST);
 198
 199        fou_parse_opt(argc, argv, &req.n, true);
 200
 201        if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
 202                return -2;
 203
 204        return 0;
 205}
 206
 207static int do_del(int argc, char **argv)
 208{
 209        FOU_REQUEST(req, 1024, FOU_CMD_DEL, NLM_F_REQUEST);
 210
 211        fou_parse_opt(argc, argv, &req.n, false);
 212
 213        if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
 214                return -2;
 215
 216        return 0;
 217}
 218
 219static int print_fou_mapping(struct nlmsghdr *n, void *arg)
 220{
 221        __u8 family = AF_INET, local_attr_type, peer_attr_type, byte_len;
 222        struct rtattr *tb[FOU_ATTR_MAX + 1];
 223        __u8 empty_buf[16] = {0};
 224        struct genlmsghdr *ghdr;
 225        int len = n->nlmsg_len;
 226
 227        if (n->nlmsg_type != genl_family)
 228                return 0;
 229
 230        len -= NLMSG_LENGTH(GENL_HDRLEN);
 231        if (len < 0)
 232                return -1;
 233
 234        ghdr = NLMSG_DATA(n);
 235        parse_rtattr(tb, FOU_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, len);
 236
 237        open_json_object(NULL);
 238        if (tb[FOU_ATTR_PORT])
 239                print_uint(PRINT_ANY, "port", "port %u",
 240                           ntohs(rta_getattr_u16(tb[FOU_ATTR_PORT])));
 241
 242        if (tb[FOU_ATTR_TYPE] &&
 243            rta_getattr_u8(tb[FOU_ATTR_TYPE]) == FOU_ENCAP_GUE)
 244                print_null(PRINT_ANY, "gue", " gue", NULL);
 245        else if (tb[FOU_ATTR_IPPROTO])
 246                print_uint(PRINT_ANY, "ipproto",
 247                           " ipproto %u", rta_getattr_u8(tb[FOU_ATTR_IPPROTO]));
 248
 249        if (tb[FOU_ATTR_AF]) {
 250                family = rta_getattr_u8(tb[FOU_ATTR_AF]);
 251
 252                print_string(PRINT_JSON, "family", NULL,
 253                             family_name(family));
 254
 255                if (family == AF_INET6)
 256                        print_string(PRINT_FP, NULL,
 257                                     " -6", NULL);
 258        }
 259
 260        local_attr_type = family == AF_INET ? FOU_ATTR_LOCAL_V4 :
 261                                              FOU_ATTR_LOCAL_V6;
 262        peer_attr_type = family == AF_INET ? FOU_ATTR_PEER_V4 :
 263                                             FOU_ATTR_PEER_V6;
 264        byte_len = af_bit_len(family) / 8;
 265
 266        if (tb[local_attr_type] && memcmp(RTA_DATA(tb[local_attr_type]),
 267                                          empty_buf, byte_len)) {
 268                print_string(PRINT_ANY, "local", " local %s",
 269                             format_host_rta(family, tb[local_attr_type]));
 270        }
 271
 272        if (tb[peer_attr_type] && memcmp(RTA_DATA(tb[peer_attr_type]),
 273                                         empty_buf, byte_len)) {
 274                print_string(PRINT_ANY, "peer", " peer %s",
 275                             format_host_rta(family, tb[peer_attr_type]));
 276        }
 277
 278        if (tb[FOU_ATTR_PEER_PORT]) {
 279                __u16 p_port = ntohs(rta_getattr_u16(tb[FOU_ATTR_PEER_PORT]));
 280
 281                if (p_port)
 282                        print_uint(PRINT_ANY, "peer_port", " peer_port %u",
 283                                   p_port);
 284
 285        }
 286
 287        if (tb[FOU_ATTR_IFINDEX]) {
 288                int index = rta_getattr_s32(tb[FOU_ATTR_IFINDEX]);
 289
 290                if (index) {
 291                        const char *ifname;
 292
 293                        ifname = ll_index_to_name(index);
 294
 295                        if (ifname)
 296                                print_string(PRINT_ANY, "dev", " dev %s",
 297                                             ifname);
 298                }
 299        }
 300
 301        print_string(PRINT_FP, NULL, "\n", NULL);
 302        close_json_object();
 303
 304        return 0;
 305}
 306
 307static int do_show(int argc, char **argv)
 308{
 309        FOU_REQUEST(req, 4096, FOU_CMD_GET, NLM_F_REQUEST | NLM_F_DUMP);
 310
 311        if (argc > 0) {
 312                fprintf(stderr,
 313                        "\"ip fou show\" does not take any arguments.\n");
 314                return -1;
 315        }
 316
 317        if (rtnl_send(&genl_rth, &req.n, req.n.nlmsg_len) < 0) {
 318                perror("Cannot send show request");
 319                exit(1);
 320        }
 321
 322        new_json_obj(json);
 323        if (rtnl_dump_filter(&genl_rth, print_fou_mapping, stdout) < 0) {
 324                fprintf(stderr, "Dump terminated\n");
 325                return 1;
 326        }
 327        delete_json_obj();
 328        fflush(stdout);
 329
 330        return 0;
 331}
 332
 333int do_ipfou(int argc, char **argv)
 334{
 335        if (argc < 1)
 336                usage();
 337
 338        if (matches(*argv, "help") == 0)
 339                usage();
 340
 341        if (genl_init_handle(&genl_rth, FOU_GENL_NAME, &genl_family))
 342                exit(1);
 343
 344        if (matches(*argv, "add") == 0)
 345                return do_add(argc-1, argv+1);
 346        if (matches(*argv, "delete") == 0)
 347                return do_del(argc-1, argv+1);
 348        if (matches(*argv, "show") == 0)
 349                return do_show(argc-1, argv+1);
 350
 351        fprintf(stderr,
 352                "Command \"%s\" is unknown, try \"ip fou help\".\n", *argv);
 353        exit(-1);
 354}
 355