Nicolas Dichtel | 1ce2de9 | 2012-12-12 10:51:47 +0100 | [diff] [blame] | 1 | /* |
| 2 | * link_iptnl.c ipip and sit driver module |
| 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: Nicolas Dichtel <nicolas.dichtel@6wind.com> |
| 10 | * |
| 11 | */ |
| 12 | |
| 13 | #include <string.h> |
| 14 | #include <net/if.h> |
| 15 | #include <sys/types.h> |
| 16 | #include <sys/socket.h> |
| 17 | #include <arpa/inet.h> |
| 18 | |
| 19 | #include <linux/ip.h> |
| 20 | #include <linux/if_tunnel.h> |
| 21 | #include "rt_names.h" |
| 22 | #include "utils.h" |
| 23 | #include "ip_common.h" |
| 24 | #include "tunnel.h" |
| 25 | |
vadimk | 561e650 | 2014-09-30 08:17:31 +0300 | [diff] [blame^] | 26 | static void print_usage(FILE *f, int sit) |
| 27 | { |
| 28 | fprintf(f, "Usage: ip link { add | set | change | replace | del } NAME\n"); |
| 29 | fprintf(f, " type { ipip | sit } [ remote ADDR ] [ local ADDR ]\n"); |
| 30 | fprintf(f, " [ ttl TTL ] [ tos TOS ] [ [no]pmtudisc ] [ dev PHYS_DEV ]\n"); |
| 31 | fprintf(f, " [ 6rd-prefix ADDR ] [ 6rd-relay_prefix ADDR ] [ 6rd-reset ]\n"); |
| 32 | if (sit) { |
| 33 | fprintf(f, " [ mode { ip6ip | ipip | any } ]\n"); |
| 34 | fprintf(f, " [ isatap ]\n"); |
| 35 | } |
| 36 | fprintf(f, "\n"); |
| 37 | fprintf(f, "Where: NAME := STRING\n"); |
| 38 | fprintf(f, " ADDR := { IP_ADDRESS | any }\n"); |
| 39 | fprintf(f, " TOS := { NUMBER | inherit }\n"); |
| 40 | fprintf(f, " TTL := { 1..255 | inherit }\n"); |
| 41 | } |
| 42 | |
Nicolas Dichtel | 1ce2de9 | 2012-12-12 10:51:47 +0100 | [diff] [blame] | 43 | static void usage(int sit) __attribute__((noreturn)); |
| 44 | static void usage(int sit) |
| 45 | { |
vadimk | 561e650 | 2014-09-30 08:17:31 +0300 | [diff] [blame^] | 46 | print_usage(stderr, sit); |
Nicolas Dichtel | 1ce2de9 | 2012-12-12 10:51:47 +0100 | [diff] [blame] | 47 | exit(-1); |
| 48 | } |
| 49 | |
| 50 | static int iptunnel_parse_opt(struct link_util *lu, int argc, char **argv, |
| 51 | struct nlmsghdr *n) |
| 52 | { |
| 53 | struct { |
| 54 | struct nlmsghdr n; |
| 55 | struct ifinfomsg i; |
| 56 | char buf[2048]; |
| 57 | } req; |
| 58 | struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1); |
| 59 | struct rtattr *tb[IFLA_MAX + 1]; |
| 60 | struct rtattr *linkinfo[IFLA_INFO_MAX+1]; |
| 61 | struct rtattr *iptuninfo[IFLA_IPTUN_MAX + 1]; |
| 62 | int len; |
| 63 | __u32 link = 0; |
| 64 | __u32 laddr = 0; |
| 65 | __u32 raddr = 0; |
| 66 | __u8 ttl = 0; |
| 67 | __u8 tos = 0; |
| 68 | __u8 pmtudisc = 1; |
| 69 | __u16 iflags = 0; |
Nicolas Dichtel | 77620be | 2013-07-16 22:54:14 +0200 | [diff] [blame] | 70 | __u8 proto = 0; |
Nicolas Dichtel | 1ce2de9 | 2012-12-12 10:51:47 +0100 | [diff] [blame] | 71 | struct in6_addr ip6rdprefix; |
| 72 | __u16 ip6rdprefixlen = 0; |
| 73 | __u32 ip6rdrelayprefix = 0; |
| 74 | __u16 ip6rdrelayprefixlen = 0; |
| 75 | |
| 76 | memset(&ip6rdprefix, 0, sizeof(ip6rdprefix)); |
| 77 | |
| 78 | if (!(n->nlmsg_flags & NLM_F_CREATE)) { |
| 79 | memset(&req, 0, sizeof(req)); |
| 80 | |
| 81 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)); |
| 82 | req.n.nlmsg_flags = NLM_F_REQUEST; |
| 83 | req.n.nlmsg_type = RTM_GETLINK; |
| 84 | req.i.ifi_family = preferred_family; |
| 85 | req.i.ifi_index = ifi->ifi_index; |
| 86 | |
| 87 | if (rtnl_talk(&rth, &req.n, 0, 0, &req.n) < 0) { |
| 88 | get_failed: |
| 89 | fprintf(stderr, |
| 90 | "Failed to get existing tunnel info.\n"); |
| 91 | return -1; |
| 92 | } |
| 93 | |
| 94 | len = req.n.nlmsg_len; |
| 95 | len -= NLMSG_LENGTH(sizeof(*ifi)); |
| 96 | if (len < 0) |
| 97 | goto get_failed; |
| 98 | |
| 99 | parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len); |
| 100 | |
| 101 | if (!tb[IFLA_LINKINFO]) |
| 102 | goto get_failed; |
| 103 | |
| 104 | parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); |
| 105 | |
| 106 | if (!linkinfo[IFLA_INFO_DATA]) |
| 107 | goto get_failed; |
| 108 | |
| 109 | parse_rtattr_nested(iptuninfo, IFLA_IPTUN_MAX, |
| 110 | linkinfo[IFLA_INFO_DATA]); |
| 111 | |
| 112 | if (iptuninfo[IFLA_IPTUN_LOCAL]) |
| 113 | laddr = rta_getattr_u32(iptuninfo[IFLA_IPTUN_LOCAL]); |
| 114 | |
| 115 | if (iptuninfo[IFLA_IPTUN_REMOTE]) |
| 116 | raddr = rta_getattr_u32(iptuninfo[IFLA_IPTUN_REMOTE]); |
| 117 | |
| 118 | if (iptuninfo[IFLA_IPTUN_TTL]) |
| 119 | ttl = rta_getattr_u8(iptuninfo[IFLA_IPTUN_TTL]); |
| 120 | |
| 121 | if (iptuninfo[IFLA_IPTUN_TOS]) |
| 122 | tos = rta_getattr_u8(iptuninfo[IFLA_IPTUN_TOS]); |
| 123 | |
| 124 | if (iptuninfo[IFLA_IPTUN_PMTUDISC]) |
| 125 | pmtudisc = |
| 126 | rta_getattr_u8(iptuninfo[IFLA_IPTUN_PMTUDISC]); |
| 127 | |
| 128 | if (iptuninfo[IFLA_IPTUN_FLAGS]) |
| 129 | iflags = rta_getattr_u16(iptuninfo[IFLA_IPTUN_FLAGS]); |
| 130 | |
| 131 | if (iptuninfo[IFLA_IPTUN_LINK]) |
| 132 | link = rta_getattr_u32(iptuninfo[IFLA_IPTUN_LINK]); |
| 133 | |
Nicolas Dichtel | 77620be | 2013-07-16 22:54:14 +0200 | [diff] [blame] | 134 | if (iptuninfo[IFLA_IPTUN_PROTO]) |
| 135 | proto = rta_getattr_u8(iptuninfo[IFLA_IPTUN_PROTO]); |
| 136 | |
Nicolas Dichtel | 1ce2de9 | 2012-12-12 10:51:47 +0100 | [diff] [blame] | 137 | if (iptuninfo[IFLA_IPTUN_6RD_PREFIX]) |
| 138 | memcpy(&ip6rdprefix, |
| 139 | RTA_DATA(iptuninfo[IFLA_IPTUN_6RD_PREFIX]), |
| 140 | sizeof(laddr)); |
| 141 | |
| 142 | if (iptuninfo[IFLA_IPTUN_6RD_PREFIXLEN]) |
| 143 | ip6rdprefixlen = |
| 144 | rta_getattr_u16(iptuninfo[IFLA_IPTUN_6RD_PREFIXLEN]); |
| 145 | |
| 146 | if (iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIX]) |
| 147 | ip6rdrelayprefix = |
| 148 | rta_getattr_u32(iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIX]); |
| 149 | |
| 150 | if (iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]) |
| 151 | ip6rdrelayprefixlen = |
| 152 | rta_getattr_u16(iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]); |
| 153 | } |
| 154 | |
| 155 | while (argc > 0) { |
| 156 | if (strcmp(*argv, "remote") == 0) { |
| 157 | NEXT_ARG(); |
| 158 | if (strcmp(*argv, "any")) |
| 159 | raddr = get_addr32(*argv); |
| 160 | else |
| 161 | raddr = 0; |
| 162 | } else if (strcmp(*argv, "local") == 0) { |
| 163 | NEXT_ARG(); |
| 164 | if (strcmp(*argv, "any")) |
| 165 | laddr = get_addr32(*argv); |
| 166 | else |
| 167 | laddr = 0; |
| 168 | } else if (matches(*argv, "dev") == 0) { |
| 169 | NEXT_ARG(); |
| 170 | link = if_nametoindex(*argv); |
| 171 | if (link == 0) |
| 172 | invarg("\"dev\" is invalid", *argv); |
| 173 | } else if (strcmp(*argv, "ttl") == 0 || |
| 174 | strcmp(*argv, "hoplimit") == 0) { |
| 175 | NEXT_ARG(); |
| 176 | if (strcmp(*argv, "inherit") != 0) { |
| 177 | if (get_u8(&ttl, *argv, 0)) |
| 178 | invarg("invalid TTL\n", *argv); |
| 179 | } else |
| 180 | ttl = 0; |
| 181 | } else if (strcmp(*argv, "tos") == 0 || |
| 182 | strcmp(*argv, "tclass") == 0 || |
| 183 | matches(*argv, "dsfield") == 0) { |
| 184 | __u32 uval; |
| 185 | NEXT_ARG(); |
| 186 | if (strcmp(*argv, "inherit") != 0) { |
| 187 | if (rtnl_dsfield_a2n(&uval, *argv)) |
| 188 | invarg("bad TOS value", *argv); |
| 189 | tos = uval; |
| 190 | } else |
| 191 | tos = 1; |
| 192 | } else if (strcmp(*argv, "nopmtudisc") == 0) { |
| 193 | pmtudisc = 0; |
| 194 | } else if (strcmp(*argv, "pmtudisc") == 0) { |
| 195 | pmtudisc = 1; |
| 196 | } else if (strcmp(lu->id, "sit") == 0 && |
| 197 | strcmp(*argv, "isatap") == 0) { |
| 198 | iflags |= SIT_ISATAP; |
Nicolas Dichtel | 77620be | 2013-07-16 22:54:14 +0200 | [diff] [blame] | 199 | } else if (strcmp(lu->id, "sit") == 0 && |
| 200 | strcmp(*argv, "mode") == 0) { |
| 201 | NEXT_ARG(); |
| 202 | if (strcmp(*argv, "ipv6/ipv4") == 0 || |
| 203 | strcmp(*argv, "ip6ip") == 0) |
| 204 | proto = IPPROTO_IPV6; |
| 205 | else if (strcmp(*argv, "ipv4/ipv4") == 0 || |
| 206 | strcmp(*argv, "ipip") == 0 || |
| 207 | strcmp(*argv, "ip4ip4") == 0) |
| 208 | proto = IPPROTO_IPIP; |
| 209 | else if (strcmp(*argv, "any/ipv4") == 0 || |
| 210 | strcmp(*argv, "any") == 0) |
| 211 | proto = 0; |
| 212 | else |
| 213 | invarg("Cannot guess tunnel mode.", *argv); |
Nicolas Dichtel | 1ce2de9 | 2012-12-12 10:51:47 +0100 | [diff] [blame] | 214 | } else if (strcmp(*argv, "6rd-prefix") == 0) { |
| 215 | inet_prefix prefix; |
| 216 | NEXT_ARG(); |
| 217 | if (get_prefix(&prefix, *argv, AF_INET6)) |
| 218 | invarg("invalid 6rd_prefix\n", *argv); |
| 219 | memcpy(&ip6rdprefix, prefix.data, 16); |
| 220 | ip6rdprefixlen = prefix.bitlen; |
| 221 | } else if (strcmp(*argv, "6rd-relay_prefix") == 0) { |
| 222 | inet_prefix prefix; |
| 223 | NEXT_ARG(); |
| 224 | if (get_prefix(&prefix, *argv, AF_INET)) |
| 225 | invarg("invalid 6rd-relay_prefix\n", *argv); |
| 226 | memcpy(&ip6rdrelayprefix, prefix.data, 4); |
| 227 | ip6rdrelayprefixlen = prefix.bitlen; |
| 228 | } else if (strcmp(*argv, "6rd-reset") == 0) { |
| 229 | inet_prefix prefix; |
| 230 | get_prefix(&prefix, "2002::", AF_INET6); |
| 231 | memcpy(&ip6rdprefix, prefix.data, 16); |
| 232 | ip6rdprefixlen = 16; |
| 233 | ip6rdrelayprefix = 0; |
| 234 | ip6rdrelayprefixlen = 0; |
| 235 | } else |
| 236 | usage(strcmp(lu->id, "sit") == 0); |
| 237 | argc--, argv++; |
| 238 | } |
| 239 | |
| 240 | if (ttl && pmtudisc == 0) { |
Richard Godbee | 30d07e9 | 2013-08-25 22:40:18 -0400 | [diff] [blame] | 241 | fprintf(stderr, "ttl != 0 and nopmtudisc are incompatible\n"); |
Nicolas Dichtel | 1ce2de9 | 2012-12-12 10:51:47 +0100 | [diff] [blame] | 242 | exit(-1); |
| 243 | } |
| 244 | |
| 245 | addattr32(n, 1024, IFLA_IPTUN_LINK, link); |
| 246 | addattr32(n, 1024, IFLA_IPTUN_LOCAL, laddr); |
| 247 | addattr32(n, 1024, IFLA_IPTUN_REMOTE, raddr); |
| 248 | addattr8(n, 1024, IFLA_IPTUN_TTL, ttl); |
| 249 | addattr8(n, 1024, IFLA_IPTUN_TOS, tos); |
| 250 | addattr8(n, 1024, IFLA_IPTUN_PMTUDISC, pmtudisc); |
| 251 | if (strcmp(lu->id, "sit") == 0) { |
| 252 | addattr16(n, 1024, IFLA_IPTUN_FLAGS, iflags); |
Nicolas Dichtel | 77620be | 2013-07-16 22:54:14 +0200 | [diff] [blame] | 253 | addattr8(n, 1024, IFLA_IPTUN_PROTO, proto); |
Nicolas Dichtel | 1ce2de9 | 2012-12-12 10:51:47 +0100 | [diff] [blame] | 254 | if (ip6rdprefixlen) { |
| 255 | addattr_l(n, 1024, IFLA_IPTUN_6RD_PREFIX, |
| 256 | &ip6rdprefix, sizeof(ip6rdprefix)); |
| 257 | addattr16(n, 1024, IFLA_IPTUN_6RD_PREFIXLEN, |
| 258 | ip6rdprefixlen); |
| 259 | addattr32(n, 1024, IFLA_IPTUN_6RD_RELAY_PREFIX, |
| 260 | ip6rdrelayprefix); |
| 261 | addattr16(n, 1024, IFLA_IPTUN_6RD_RELAY_PREFIXLEN, |
| 262 | ip6rdrelayprefixlen); |
| 263 | } |
| 264 | } |
| 265 | |
| 266 | return 0; |
| 267 | } |
| 268 | |
| 269 | static void iptunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) |
| 270 | { |
| 271 | char s1[1024]; |
| 272 | char s2[64]; |
| 273 | const char *local = "any"; |
| 274 | const char *remote = "any"; |
| 275 | |
| 276 | if (!tb) |
| 277 | return; |
| 278 | |
| 279 | if (tb[IFLA_IPTUN_REMOTE]) { |
| 280 | unsigned addr = rta_getattr_u32(tb[IFLA_IPTUN_REMOTE]); |
| 281 | |
| 282 | if (addr) |
| 283 | remote = format_host(AF_INET, 4, &addr, s1, sizeof(s1)); |
| 284 | } |
| 285 | |
| 286 | fprintf(f, "remote %s ", remote); |
| 287 | |
| 288 | if (tb[IFLA_IPTUN_LOCAL]) { |
| 289 | unsigned addr = rta_getattr_u32(tb[IFLA_IPTUN_LOCAL]); |
| 290 | |
| 291 | if (addr) |
| 292 | local = format_host(AF_INET, 4, &addr, s1, sizeof(s1)); |
| 293 | } |
| 294 | |
| 295 | fprintf(f, "local %s ", local); |
| 296 | |
| 297 | if (tb[IFLA_IPTUN_LINK] && rta_getattr_u32(tb[IFLA_IPTUN_LINK])) { |
| 298 | unsigned link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]); |
| 299 | const char *n = if_indextoname(link, s2); |
| 300 | |
| 301 | if (n) |
| 302 | fprintf(f, "dev %s ", n); |
| 303 | else |
| 304 | fprintf(f, "dev %u ", link); |
| 305 | } |
| 306 | |
| 307 | if (tb[IFLA_IPTUN_TTL] && rta_getattr_u8(tb[IFLA_IPTUN_TTL])) |
| 308 | fprintf(f, "ttl %d ", rta_getattr_u8(tb[IFLA_IPTUN_TTL])); |
| 309 | else |
| 310 | fprintf(f, "ttl inherit "); |
| 311 | |
| 312 | if (tb[IFLA_IPTUN_TOS] && rta_getattr_u8(tb[IFLA_IPTUN_TOS])) { |
| 313 | int tos = rta_getattr_u8(tb[IFLA_IPTUN_TOS]); |
| 314 | |
| 315 | fputs("tos ", f); |
| 316 | if (tos == 1) |
| 317 | fputs("inherit ", f); |
| 318 | else |
| 319 | fprintf(f, "0x%x ", tos); |
| 320 | } |
| 321 | |
| 322 | if (tb[IFLA_IPTUN_PMTUDISC] && rta_getattr_u8(tb[IFLA_IPTUN_PMTUDISC])) |
| 323 | fprintf(f, "pmtudisc "); |
| 324 | else |
| 325 | fprintf(f, "nopmtudisc "); |
| 326 | |
| 327 | if (tb[IFLA_IPTUN_FLAGS]) { |
Nicolas Dichtel | 195f0f6 | 2012-12-14 09:50:33 -0800 | [diff] [blame] | 328 | __u16 iflags = rta_getattr_u16(tb[IFLA_IPTUN_FLAGS]); |
Nicolas Dichtel | 1ce2de9 | 2012-12-12 10:51:47 +0100 | [diff] [blame] | 329 | |
Nicolas Dichtel | 195f0f6 | 2012-12-14 09:50:33 -0800 | [diff] [blame] | 330 | if (iflags & SIT_ISATAP) |
| 331 | fprintf(f, "isatap "); |
Nicolas Dichtel | 1ce2de9 | 2012-12-12 10:51:47 +0100 | [diff] [blame] | 332 | } |
| 333 | |
| 334 | if (tb[IFLA_IPTUN_6RD_PREFIXLEN] && |
| 335 | *(__u16 *)RTA_DATA(tb[IFLA_IPTUN_6RD_PREFIXLEN])) { |
| 336 | __u16 prefixlen = rta_getattr_u16(tb[IFLA_IPTUN_6RD_PREFIXLEN]); |
| 337 | __u16 relayprefixlen = |
| 338 | rta_getattr_u16(tb[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]); |
| 339 | __u32 relayprefix = |
| 340 | rta_getattr_u32(tb[IFLA_IPTUN_6RD_RELAY_PREFIX]); |
| 341 | |
| 342 | printf("6rd-prefix %s/%u ", |
| 343 | inet_ntop(AF_INET6, RTA_DATA(tb[IFLA_IPTUN_6RD_PREFIX]), |
Nicolas Dichtel | 195f0f6 | 2012-12-14 09:50:33 -0800 | [diff] [blame] | 344 | s1, sizeof(s1)), |
Nicolas Dichtel | 1ce2de9 | 2012-12-12 10:51:47 +0100 | [diff] [blame] | 345 | prefixlen); |
| 346 | if (relayprefix) { |
| 347 | printf("6rd-relay_prefix %s/%u ", |
| 348 | format_host(AF_INET, 4, &relayprefix, s1, |
Nicolas Dichtel | 195f0f6 | 2012-12-14 09:50:33 -0800 | [diff] [blame] | 349 | sizeof(s1)), |
Nicolas Dichtel | 1ce2de9 | 2012-12-12 10:51:47 +0100 | [diff] [blame] | 350 | relayprefixlen); |
| 351 | } |
| 352 | } |
| 353 | } |
| 354 | |
vadimk | 561e650 | 2014-09-30 08:17:31 +0300 | [diff] [blame^] | 355 | static void iptunnel_print_help(struct link_util *lu, int argc, char **argv, |
| 356 | FILE *f) |
| 357 | { |
| 358 | print_usage(f, strcmp(lu->id, "sit") == 0); |
| 359 | } |
| 360 | |
Nicolas Dichtel | 1ce2de9 | 2012-12-12 10:51:47 +0100 | [diff] [blame] | 361 | struct link_util ipip_link_util = { |
| 362 | .id = "ipip", |
| 363 | .maxattr = IFLA_IPTUN_MAX, |
| 364 | .parse_opt = iptunnel_parse_opt, |
| 365 | .print_opt = iptunnel_print_opt, |
vadimk | 561e650 | 2014-09-30 08:17:31 +0300 | [diff] [blame^] | 366 | .print_help = iptunnel_print_help, |
Nicolas Dichtel | 1ce2de9 | 2012-12-12 10:51:47 +0100 | [diff] [blame] | 367 | }; |
| 368 | |
| 369 | struct link_util sit_link_util = { |
| 370 | .id = "sit", |
| 371 | .maxattr = IFLA_IPTUN_MAX, |
| 372 | .parse_opt = iptunnel_parse_opt, |
| 373 | .print_opt = iptunnel_print_opt, |
vadimk | 561e650 | 2014-09-30 08:17:31 +0300 | [diff] [blame^] | 374 | .print_help = iptunnel_print_help, |
Nicolas Dichtel | 1ce2de9 | 2012-12-12 10:51:47 +0100 | [diff] [blame] | 375 | }; |