/* * iplink_geneve.c GENEVE device support * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Authors: John W. Linville */ #include #include "rt_names.h" #include "utils.h" #include "ip_common.h" static void print_explain(FILE *f) { fprintf(f, "Usage: ... geneve id VNI remote ADDR\n"); fprintf(f, " [ ttl TTL ] [ tos TOS ] [ flowlabel LABEL ]\n"); fprintf(f, " [ dstport PORT ] [ [no]external ]\n"); fprintf(f, " [ [no]udpcsum ] [ [no]udp6zerocsumtx ] [ [no]udp6zerocsumrx ]\n"); fprintf(f, "\n"); fprintf(f, "Where: VNI := 0-16777215\n"); fprintf(f, " ADDR := IP_ADDRESS\n"); fprintf(f, " TOS := { NUMBER | inherit }\n"); fprintf(f, " TTL := { 1..255 | inherit }\n"); fprintf(f, " LABEL := 0-1048575\n"); } static void explain(void) { print_explain(stderr); } static int geneve_parse_opt(struct link_util *lu, int argc, char **argv, struct nlmsghdr *n) { __u32 vni = 0; int vni_set = 0; __u32 daddr = 0; struct in6_addr daddr6 = IN6ADDR_ANY_INIT; __u32 label = 0; __u8 ttl = 0; __u8 tos = 0; __u16 dstport = 0; bool metadata = 0; __u8 udpcsum = 0; bool udpcsum_set = false; __u8 udp6zerocsumtx = 0; bool udp6zerocsumtx_set = false; __u8 udp6zerocsumrx = 0; bool udp6zerocsumrx_set = false; while (argc > 0) { if (!matches(*argv, "id") || !matches(*argv, "vni")) { NEXT_ARG(); if (get_u32(&vni, *argv, 0) || vni >= 1u << 24) invarg("invalid id", *argv); vni_set = 1; } else if (!matches(*argv, "remote")) { NEXT_ARG(); if (!inet_get_addr(*argv, &daddr, &daddr6)) { fprintf(stderr, "Invalid address \"%s\"\n", *argv); return -1; } if (IN6_IS_ADDR_MULTICAST(&daddr6) || IN_MULTICAST(ntohl(daddr))) invarg("invalid remote address", *argv); } else if (!matches(*argv, "ttl") || !matches(*argv, "hoplimit")) { unsigned int uval; NEXT_ARG(); if (strcmp(*argv, "inherit") != 0) { if (get_unsigned(&uval, *argv, 0)) invarg("invalid TTL", *argv); if (uval > 255) invarg("TTL must be <= 255", *argv); ttl = uval; } } else if (!matches(*argv, "tos") || !matches(*argv, "dsfield")) { __u32 uval; NEXT_ARG(); if (strcmp(*argv, "inherit") != 0) { if (rtnl_dsfield_a2n(&uval, *argv)) invarg("bad TOS value", *argv); tos = uval; } else tos = 1; } else if (!matches(*argv, "label") || !matches(*argv, "flowlabel")) { __u32 uval; NEXT_ARG(); if (get_u32(&uval, *argv, 0) || (uval & ~LABEL_MAX_MASK)) invarg("invalid flowlabel", *argv); label = htonl(uval); } else if (!matches(*argv, "dstport")) { NEXT_ARG(); if (get_u16(&dstport, *argv, 0)) invarg("dstport", *argv); } else if (!matches(*argv, "external")) { metadata = true; } else if (!matches(*argv, "noexternal")) { metadata = false; } else if (!matches(*argv, "udpcsum")) { udpcsum = 1; udpcsum_set = true; } else if (!matches(*argv, "noudpcsum")) { udpcsum = 0; udpcsum_set = true; } else if (!matches(*argv, "udp6zerocsumtx")) { udp6zerocsumtx = 1; udp6zerocsumtx_set = true; } else if (!matches(*argv, "noudp6zerocsumtx")) { udp6zerocsumtx = 0; udp6zerocsumtx_set = true; } else if (!matches(*argv, "udp6zerocsumrx")) { udp6zerocsumrx = 1; udp6zerocsumrx_set = true; } else if (!matches(*argv, "noudp6zerocsumrx")) { udp6zerocsumrx = 0; udp6zerocsumrx_set = true; } else if (matches(*argv, "help") == 0) { explain(); return -1; } else { fprintf(stderr, "geneve: unknown command \"%s\"?\n", *argv); explain(); return -1; } argc--, argv++; } if (metadata && vni_set) { fprintf(stderr, "geneve: both 'external' and vni cannot be specified\n"); return -1; } if (!metadata) { /* parameter checking make sense only for full geneve tunnels */ if (!vni_set) { fprintf(stderr, "geneve: missing virtual network identifier\n"); return -1; } if (!daddr && IN6_IS_ADDR_UNSPECIFIED(&daddr6)) { fprintf(stderr, "geneve: remote link partner not specified\n"); return -1; } } addattr32(n, 1024, IFLA_GENEVE_ID, vni); if (daddr) addattr_l(n, 1024, IFLA_GENEVE_REMOTE, &daddr, 4); if (!IN6_IS_ADDR_UNSPECIFIED(&daddr6)) addattr_l(n, 1024, IFLA_GENEVE_REMOTE6, &daddr6, sizeof(struct in6_addr)); addattr32(n, 1024, IFLA_GENEVE_LABEL, label); addattr8(n, 1024, IFLA_GENEVE_TTL, ttl); addattr8(n, 1024, IFLA_GENEVE_TOS, tos); if (dstport) addattr16(n, 1024, IFLA_GENEVE_PORT, htons(dstport)); if (metadata) addattr(n, 1024, IFLA_GENEVE_COLLECT_METADATA); if (udpcsum_set) addattr8(n, 1024, IFLA_GENEVE_UDP_CSUM, udpcsum); if (udp6zerocsumtx_set) addattr8(n, 1024, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, udp6zerocsumtx); if (udp6zerocsumrx_set) addattr8(n, 1024, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, udp6zerocsumrx); return 0; } static void geneve_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) { __u32 vni; __u8 tos; if (!tb) return; if (!tb[IFLA_GENEVE_ID] || RTA_PAYLOAD(tb[IFLA_GENEVE_ID]) < sizeof(__u32)) return; vni = rta_getattr_u32(tb[IFLA_GENEVE_ID]); fprintf(f, "id %u ", vni); if (tb[IFLA_GENEVE_REMOTE]) { __be32 addr = rta_getattr_u32(tb[IFLA_GENEVE_REMOTE]); if (addr) fprintf(f, "remote %s ", format_host(AF_INET, 4, &addr)); } else if (tb[IFLA_GENEVE_REMOTE6]) { struct in6_addr addr; memcpy(&addr, RTA_DATA(tb[IFLA_GENEVE_REMOTE6]), sizeof(struct in6_addr)); if (!IN6_IS_ADDR_UNSPECIFIED(&addr)) { if (!IN6_IS_ADDR_MULTICAST(&addr)) fprintf(f, "remote %s ", format_host(AF_INET6, sizeof(struct in6_addr), &addr)); } } if (tb[IFLA_GENEVE_TTL]) { __u8 ttl = rta_getattr_u8(tb[IFLA_GENEVE_TTL]); if (ttl) fprintf(f, "ttl %d ", ttl); } if (tb[IFLA_GENEVE_TOS] && (tos = rta_getattr_u8(tb[IFLA_GENEVE_TOS]))) { if (tos == 1) fprintf(f, "tos inherit "); else fprintf(f, "tos %#x ", tos); } if (tb[IFLA_GENEVE_LABEL]) { __u32 label = rta_getattr_u32(tb[IFLA_GENEVE_LABEL]); if (label) fprintf(f, "flowlabel %#x ", ntohl(label)); } if (tb[IFLA_GENEVE_PORT]) fprintf(f, "dstport %u ", ntohs(rta_getattr_u16(tb[IFLA_GENEVE_PORT]))); if (tb[IFLA_GENEVE_COLLECT_METADATA]) fputs("external ", f); if (tb[IFLA_GENEVE_UDP_CSUM]) { if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_CSUM])) fputs("no", f); fputs("udpcsum ", f); } if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]) { if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX])) fputs("no", f); fputs("udp6zerocsumtx ", f); } if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]) { if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX])) fputs("no", f); fputs("udp6zerocsumrx ", f); } } static void geneve_print_help(struct link_util *lu, int argc, char **argv, FILE *f) { print_explain(f); } struct link_util geneve_link_util = { .id = "geneve", .maxattr = IFLA_GENEVE_MAX, .parse_opt = geneve_parse_opt, .print_opt = geneve_print_opt, .print_help = geneve_print_help, };