1 /* 2 * lib/route/link/ip6tnl.c IP6TNL Link Info 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation version 2.1 7 * of the License. 8 * 9 * Copyright (c) 2014 Susant Sahani <susant (at) redhat.com> 10 */ 11 12 /** 13 * @ingroup link 14 * @defgroup ip6tnl IP6TNL 15 * ip6tnl link module 16 * 17 * @details 18 * \b Link Type Name: "ip6tnl" 19 * 20 * @route_doc{link_ip6tnl, IP6TNL Documentation} 21 * 22 * @{ 23 */ 24 25 #include <netlink-private/netlink.h> 26 #include <netlink/netlink.h> 27 #include <netlink/attr.h> 28 #include <netlink/utils.h> 29 #include <netlink/object.h> 30 #include <netlink/route/rtnl.h> 31 #include <netlink-private/route/link/api.h> 32 #include <linux/if_tunnel.h> 33 #include <netinet/in.h> 34 35 #define IP6_TNL_ATTR_LINK (1 << 0) 36 #define IP6_TNL_ATTR_LOCAL (1 << 1) 37 #define IP6_TNL_ATTR_REMOTE (1 << 2) 38 #define IP6_TNL_ATTR_TTL (1 << 3) 39 #define IP6_TNL_ATTR_TOS (1 << 4) 40 #define IP6_TNL_ATTR_ENCAPLIMIT (1 << 5) 41 #define IP6_TNL_ATTR_FLAGS (1 << 6) 42 #define IP6_TNL_ATTR_PROTO (1 << 7) 43 #define IP6_TNL_ATTR_FLOWINFO (1 << 8) 44 45 struct ip6_tnl_info 46 { 47 uint8_t ttl; 48 uint8_t tos; 49 uint8_t encap_limit; 50 uint8_t proto; 51 uint32_t flags; 52 uint32_t link; 53 uint32_t flowinfo; 54 struct in6_addr local; 55 struct in6_addr remote; 56 uint32_t ip6_tnl_mask; 57 }; 58 59 static struct nla_policy ip6_tnl_policy[IFLA_IPTUN_MAX + 1] = { 60 [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, 61 [IFLA_IPTUN_LOCAL] = { .minlen = sizeof(struct in6_addr) }, 62 [IFLA_IPTUN_REMOTE] = { .minlen = sizeof(struct in6_addr) }, 63 [IFLA_IPTUN_TTL] = { .type = NLA_U8 }, 64 [IFLA_IPTUN_TOS] = { .type = NLA_U8 }, 65 [IFLA_IPTUN_ENCAP_LIMIT] = { .type = NLA_U8 }, 66 [IFLA_IPTUN_FLOWINFO] = { .type = NLA_U32 }, 67 [IFLA_IPTUN_FLAGS] = { .type = NLA_U32 }, 68 [IFLA_IPTUN_PROTO] = { .type = NLA_U8 }, 69 }; 70 71 static int ip6_tnl_alloc(struct rtnl_link *link) 72 { 73 struct ip6_tnl_info *ip6_tnl; 74 75 ip6_tnl = calloc(1, sizeof(*ip6_tnl)); 76 if (!ip6_tnl) 77 return -NLE_NOMEM; 78 79 link->l_info = ip6_tnl; 80 81 return 0; 82 } 83 84 static int ip6_tnl_parse(struct rtnl_link *link, struct nlattr *data, 85 struct nlattr *xstats) 86 { 87 struct nlattr *tb[IFLA_IPTUN_MAX + 1]; 88 struct ip6_tnl_info *ip6_tnl; 89 int err; 90 91 NL_DBG(3, "Parsing IP6_TNL link info"); 92 93 err = nla_parse_nested(tb, IFLA_IPTUN_MAX, data, ip6_tnl_policy); 94 if (err < 0) 95 goto errout; 96 97 err = ip6_tnl_alloc(link); 98 if (err < 0) 99 goto errout; 100 101 ip6_tnl = link->l_info; 102 103 if (tb[IFLA_IPTUN_LINK]) { 104 ip6_tnl->link = nla_get_u32(tb[IFLA_IPTUN_LINK]); 105 ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_LINK; 106 } 107 108 if (tb[IFLA_IPTUN_LOCAL]) { 109 nla_memcpy(&ip6_tnl->local, tb[IFLA_IPTUN_LOCAL], sizeof(struct in6_addr)); 110 ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_LOCAL; 111 } 112 113 if (tb[IFLA_IPTUN_REMOTE]) { 114 nla_memcpy(&ip6_tnl->remote, tb[IFLA_IPTUN_REMOTE], sizeof(struct in6_addr)); 115 ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_REMOTE; 116 } 117 118 if (tb[IFLA_IPTUN_TTL]) { 119 ip6_tnl->ttl = nla_get_u8(tb[IFLA_IPTUN_TTL]); 120 ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_TTL; 121 } 122 123 if (tb[IFLA_IPTUN_TOS]) { 124 ip6_tnl->tos = nla_get_u8(tb[IFLA_IPTUN_TOS]); 125 ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_TOS; 126 } 127 128 if (tb[IFLA_IPTUN_ENCAP_LIMIT]) { 129 ip6_tnl->encap_limit = nla_get_u8(tb[IFLA_IPTUN_ENCAP_LIMIT]); 130 ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_ENCAPLIMIT; 131 } 132 133 if (tb[IFLA_IPTUN_FLAGS]) { 134 ip6_tnl->flags = nla_get_u32(tb[IFLA_IPTUN_FLAGS]); 135 ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_FLAGS; 136 } 137 138 if (tb[IFLA_IPTUN_FLOWINFO]) { 139 ip6_tnl->flowinfo = nla_get_u32(tb[IFLA_IPTUN_FLOWINFO]); 140 ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_FLOWINFO; 141 } 142 143 if (tb[IFLA_IPTUN_PROTO]) { 144 ip6_tnl->proto = nla_get_u8(tb[IFLA_IPTUN_PROTO]); 145 ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_PROTO; 146 } 147 148 err = 0; 149 150 errout: 151 return err; 152 } 153 154 static int ip6_tnl_put_attrs(struct nl_msg *msg, struct rtnl_link *link) 155 { 156 struct ip6_tnl_info *ip6_tnl = link->l_info; 157 struct nlattr *data; 158 159 data = nla_nest_start(msg, IFLA_INFO_DATA); 160 if (!data) 161 return -NLE_MSGSIZE; 162 163 if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_LINK) 164 NLA_PUT_U32(msg, IFLA_IPTUN_LINK, ip6_tnl->link); 165 166 if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_LOCAL) 167 NLA_PUT(msg, IFLA_IPTUN_LOCAL, sizeof(struct in6_addr), &ip6_tnl->local); 168 169 if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_REMOTE) 170 NLA_PUT(msg, IFLA_IPTUN_REMOTE, sizeof(struct in6_addr), &ip6_tnl->remote); 171 172 if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_TTL) 173 NLA_PUT_U8(msg, IFLA_IPTUN_TTL, ip6_tnl->ttl); 174 175 if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_TOS) 176 NLA_PUT_U8(msg, IFLA_IPTUN_TOS, ip6_tnl->tos); 177 178 if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_ENCAPLIMIT) 179 NLA_PUT_U8(msg, IFLA_IPTUN_ENCAP_LIMIT, ip6_tnl->encap_limit); 180 181 if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_FLAGS) 182 NLA_PUT_U32(msg, IFLA_IPTUN_FLAGS, ip6_tnl->flags); 183 184 if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_FLOWINFO) 185 NLA_PUT_U32(msg, IFLA_IPTUN_FLOWINFO, ip6_tnl->flowinfo); 186 187 /* kernel crashes if this attribure is missing temporary fix */ 188 if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_PROTO) 189 NLA_PUT_U8(msg, IFLA_IPTUN_PROTO, ip6_tnl->proto); 190 else 191 NLA_PUT_U8(msg, IFLA_IPTUN_PROTO, 0); 192 193 nla_nest_end(msg, data); 194 195 nla_put_failure: 196 return 0; 197 } 198 199 static void ip6_tnl_free(struct rtnl_link *link) 200 { 201 struct ip6_tnl_info *ip6_tnl = link->l_info; 202 203 free(ip6_tnl); 204 link->l_info = NULL; 205 } 206 207 static void ip6_tnl_dump_line(struct rtnl_link *link, struct nl_dump_params *p) 208 { 209 nl_dump(p, "ip6_tnl : %s", link->l_name); 210 } 211 212 static void ip6_tnl_dump_details(struct rtnl_link *link, struct nl_dump_params *p) 213 { 214 struct ip6_tnl_info *ip6_tnl = link->l_info; 215 char *name, addr[INET6_ADDRSTRLEN]; 216 217 if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_LINK) { 218 nl_dump(p, " link "); 219 name = rtnl_link_get_name(link); 220 if (name) 221 nl_dump_line(p, "%s\n", name); 222 else 223 nl_dump_line(p, "%u\n", ip6_tnl->link); 224 } 225 226 if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_LOCAL) { 227 nl_dump(p, " local "); 228 229 if(inet_ntop(AF_INET6, &ip6_tnl->local, addr, INET6_ADDRSTRLEN)) 230 nl_dump_line(p, "%s\n", addr); 231 else 232 nl_dump_line(p, "%#x\n", ip6_tnl->local); 233 } 234 235 if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_REMOTE) { 236 nl_dump(p, " remote "); 237 238 if(inet_ntop(AF_INET6, &ip6_tnl->remote, addr, INET6_ADDRSTRLEN)) 239 nl_dump_line(p, "%s\n", addr); 240 else 241 nl_dump_line(p, "%#x\n", ip6_tnl->remote); 242 } 243 244 if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_TTL) { 245 nl_dump(p, " ttl "); 246 nl_dump_line(p, "%d\n", ip6_tnl->ttl); 247 } 248 249 if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_TOS) { 250 nl_dump(p, " tos "); 251 nl_dump_line(p, "%d\n", ip6_tnl->tos); 252 } 253 254 if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_ENCAPLIMIT) { 255 nl_dump(p, " encaplimit "); 256 nl_dump_line(p, "%d\n", ip6_tnl->encap_limit); 257 } 258 259 if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_FLAGS) { 260 nl_dump(p, " flags "); 261 nl_dump_line(p, " (%x)\n", ip6_tnl->flags); 262 } 263 264 if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_FLOWINFO) { 265 nl_dump(p, " flowinfo "); 266 nl_dump_line(p, " (%x)\n", ip6_tnl->flowinfo); 267 } 268 269 if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_PROTO) { 270 nl_dump(p, " proto "); 271 nl_dump_line(p, " (%x)\n", ip6_tnl->proto); 272 } 273 } 274 275 static int ip6_tnl_clone(struct rtnl_link *dst, struct rtnl_link *src) 276 { 277 struct ip6_tnl_info *ip6_tnl_dst, *ip6_tnl_src = src->l_info; 278 int err; 279 280 dst->l_info = NULL; 281 282 err = rtnl_link_set_type(dst, "ip6tnl"); 283 if (err < 0) 284 return err; 285 286 ip6_tnl_dst = dst->l_info; 287 288 if (!ip6_tnl_dst || !ip6_tnl_src) 289 BUG(); 290 291 memcpy(ip6_tnl_dst, ip6_tnl_src, sizeof(struct ip6_tnl_info)); 292 293 return 0; 294 } 295 296 static struct rtnl_link_info_ops ip6_tnl_info_ops = { 297 .io_name = "ip6tnl", 298 .io_alloc = ip6_tnl_alloc, 299 .io_parse = ip6_tnl_parse, 300 .io_dump = { 301 [NL_DUMP_LINE] = ip6_tnl_dump_line, 302 [NL_DUMP_DETAILS] = ip6_tnl_dump_details, 303 }, 304 .io_clone = ip6_tnl_clone, 305 .io_put_attrs = ip6_tnl_put_attrs, 306 .io_free = ip6_tnl_free, 307 }; 308 309 #define IS_IP6_TNL_LINK_ASSERT(link)\ 310 if ((link)->l_info_ops != &ip6_tnl_info_ops) {\ 311 APPBUG("Link is not a ip6_tnl link. set type \"ip6tnl\" first.");\ 312 return -NLE_OPNOTSUPP;\ 313 } 314 315 struct rtnl_link *rtnl_link_ip6_tnl_alloc(void) 316 { 317 struct rtnl_link *link; 318 int err; 319 320 link = rtnl_link_alloc(); 321 if (!link) 322 return NULL; 323 324 err = rtnl_link_set_type(link, "ip6tnl"); 325 if (err < 0) { 326 rtnl_link_put(link); 327 return NULL; 328 } 329 330 return link; 331 } 332 333 /** 334 * Check if link is a IP6_TNL link 335 * @arg link Link object 336 * 337 * @return True if link is a IP6_TNL link, otherwise false is returned. 338 */ 339 int rtnl_link_is_ip6_tnl(struct rtnl_link *link) 340 { 341 return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "ip6tnl"); 342 } 343 344 /** 345 * Create a new ip6_tnl tunnel device 346 * @arg sock netlink socket 347 * @arg name name of the tunnel device 348 * 349 * Creates a new ip6_tnl tunnel device in the kernel 350 * @return 0 on success or a negative error code 351 */ 352 int rtnl_link_ip6_tnl_add(struct nl_sock *sk, const char *name) 353 { 354 struct rtnl_link *link; 355 int err; 356 357 link = rtnl_link_ip6_tnl_alloc(); 358 if (!link) 359 return -NLE_NOMEM; 360 361 if(name) 362 rtnl_link_set_name(link, name); 363 364 err = rtnl_link_add(sk, link, NLM_F_CREATE); 365 rtnl_link_put(link); 366 367 return err; 368 } 369 370 /** 371 * Set IP6_TNL tunnel interface index 372 * @arg link Link object 373 * @arg index interface index 374 * 375 * @return 0 on success or a negative error code 376 */ 377 int rtnl_link_ip6_tnl_set_link(struct rtnl_link *link, uint32_t index) 378 { 379 struct ip6_tnl_info *ip6_tnl = link->l_info; 380 381 IS_IP6_TNL_LINK_ASSERT(link); 382 383 ip6_tnl->link = index; 384 ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_LINK; 385 386 return 0; 387 } 388 389 /** 390 * Get IP6_TNL tunnel interface index 391 * @arg link Link object 392 * 393 * @return interface index value 394 */ 395 uint32_t rtnl_link_ip6_tnl_get_link(struct rtnl_link *link) 396 { 397 struct ip6_tnl_info *ip6_tnl = link->l_info; 398 399 IS_IP6_TNL_LINK_ASSERT(link); 400 401 return ip6_tnl->link; 402 } 403 404 /** 405 * Set IP6_TNL tunnel local address 406 * @arg link Link object 407 * @arg addr local address 408 * 409 * @return 0 on success or a negative error code 410 */ 411 int rtnl_link_ip6_tnl_set_local(struct rtnl_link *link, struct in6_addr *addr) 412 { 413 struct ip6_tnl_info *ip6_tnl = link->l_info; 414 415 IS_IP6_TNL_LINK_ASSERT(link); 416 417 memcpy(&ip6_tnl->local, addr, sizeof(struct in6_addr)); 418 ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_LOCAL; 419 420 return 0; 421 } 422 423 /** 424 * Get IP6_TNL tunnel local address 425 * @arg link Link object 426 * 427 * @return 0 on success or a negative error code 428 */ 429 int rtnl_link_ip6_tnl_get_local(struct rtnl_link *link, struct in6_addr *addr) 430 { 431 struct ip6_tnl_info *ip6_tnl = link->l_info; 432 433 IS_IP6_TNL_LINK_ASSERT(link); 434 435 memcpy(addr, &ip6_tnl->local, sizeof(struct in6_addr)); 436 437 return 0; 438 } 439 440 /** 441 * Set IP6_TNL tunnel remote address 442 * @arg link Link object 443 * @arg remote remote address 444 * 445 * @return 0 on success or a negative error code 446 */ 447 int rtnl_link_ip6_tnl_set_remote(struct rtnl_link *link, struct in6_addr *addr) 448 { 449 struct ip6_tnl_info *ip6_tnl = link->l_info; 450 451 IS_IP6_TNL_LINK_ASSERT(link); 452 453 memcpy(&ip6_tnl->remote, addr, sizeof(struct in6_addr)); 454 ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_REMOTE; 455 456 return 0; 457 } 458 459 /** 460 * Get IP6_TNL tunnel remote address 461 * @arg link Link object 462 * 463 * @return 0 on success or a negative error code 464 */ 465 int rtnl_link_ip6_tnl_get_remote(struct rtnl_link *link, struct in6_addr *addr) 466 { 467 struct ip6_tnl_info *ip6_tnl = link->l_info; 468 469 IS_IP6_TNL_LINK_ASSERT(link); 470 471 memcpy(addr, &ip6_tnl->remote, sizeof(struct in6_addr)); 472 473 return 0; 474 } 475 476 /** 477 * Set IP6_TNL tunnel ttl 478 * @arg link Link object 479 * @arg ttl tunnel ttl 480 * 481 * @return 0 on success or a negative error code 482 */ 483 int rtnl_link_ip6_tnl_set_ttl(struct rtnl_link *link, uint8_t ttl) 484 { 485 struct ip6_tnl_info *ip6_tnl = link->l_info; 486 487 IS_IP6_TNL_LINK_ASSERT(link); 488 489 ip6_tnl->ttl = ttl; 490 ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_TTL; 491 492 return 0; 493 } 494 495 /** 496 * Get IP6_TNL tunnel ttl 497 * @arg link Link object 498 * 499 * @return ttl value 500 */ 501 uint8_t rtnl_link_ip6_tnl_get_ttl(struct rtnl_link *link) 502 { 503 struct ip6_tnl_info *ip6_tnl = link->l_info; 504 505 IS_IP6_TNL_LINK_ASSERT(link); 506 507 return ip6_tnl->ttl; 508 } 509 510 /** 511 * Set IP6_TNL tunnel tos 512 * @arg link Link object 513 * @arg tos tunnel tos 514 * 515 * @return 0 on success or a negative error code 516 */ 517 int rtnl_link_ip6_tnl_set_tos(struct rtnl_link *link, uint8_t tos) 518 { 519 struct ip6_tnl_info *ip6_tnl = link->l_info; 520 521 IS_IP6_TNL_LINK_ASSERT(link); 522 523 ip6_tnl->tos = tos; 524 ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_TOS; 525 526 return 0; 527 } 528 529 /** 530 * Get IP6_TNL tunnel tos 531 * @arg link Link object 532 * 533 * @return tos value 534 */ 535 uint8_t rtnl_link_ip6_tnl_get_tos(struct rtnl_link *link) 536 { 537 struct ip6_tnl_info *ip6_tnl = link->l_info; 538 539 IS_IP6_TNL_LINK_ASSERT(link); 540 541 return ip6_tnl->tos; 542 } 543 544 /** 545 * Set IP6_TNL tunnel encap limit 546 * @arg link Link object 547 * @arg encap_limit encaplimit value 548 * 549 * @return 0 on success or a negative error code 550 */ 551 int rtnl_link_ip6_tnl_set_encaplimit(struct rtnl_link *link, uint8_t encap_limit) 552 { 553 struct ip6_tnl_info *ip6_tnl = link->l_info; 554 555 IS_IP6_TNL_LINK_ASSERT(link); 556 557 ip6_tnl->encap_limit = encap_limit; 558 ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_ENCAPLIMIT; 559 560 return 0; 561 } 562 563 /** 564 * Get IP6_TNL encaplimit 565 * @arg link Link object 566 * 567 * @return encaplimit value 568 */ 569 uint8_t rtnl_link_ip6_tnl_get_encaplimit(struct rtnl_link *link) 570 { 571 struct ip6_tnl_info *ip6_tnl = link->l_info; 572 573 IS_IP6_TNL_LINK_ASSERT(link); 574 575 return ip6_tnl->encap_limit; 576 } 577 578 /** 579 * Set IP6_TNL tunnel flowinfo 580 * @arg link Link object 581 * @arg flowinfo flowinfo value 582 * 583 * @return 0 on success or a negative error code 584 */ 585 int rtnl_link_ip6_tnl_set_flowinfo(struct rtnl_link *link, uint32_t flowinfo) 586 { 587 struct ip6_tnl_info *ip6_tnl = link->l_info; 588 589 IS_IP6_TNL_LINK_ASSERT(link); 590 591 ip6_tnl->flowinfo = flowinfo; 592 ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_FLOWINFO; 593 594 return 0; 595 } 596 597 /** 598 * Get IP6_TNL flowinfo 599 * @arg link Link object 600 * 601 * @return flowinfo value 602 */ 603 uint32_t rtnl_link_ip6_tnl_get_flowinfo(struct rtnl_link *link) 604 { 605 struct ip6_tnl_info *ip6_tnl = link->l_info; 606 607 IS_IP6_TNL_LINK_ASSERT(link); 608 609 return ip6_tnl->flowinfo; 610 } 611 612 /** 613 * Set IP6_TNL tunnel flags 614 * @arg link Link object 615 * @arg flags tunnel flags 616 * 617 * @return 0 on success or a negative error code 618 */ 619 int rtnl_link_ip6_tnl_set_flags(struct rtnl_link *link, uint32_t flags) 620 { 621 struct ip6_tnl_info *ip6_tnl = link->l_info; 622 623 IS_IP6_TNL_LINK_ASSERT(link); 624 625 ip6_tnl->flags = flags; 626 ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_FLAGS; 627 628 return 0; 629 } 630 631 /** 632 * Get IP6_TNL path flags 633 * @arg link Link object 634 * 635 * @return flags value 636 */ 637 uint32_t rtnl_link_ip6_tnl_get_flags(struct rtnl_link *link) 638 { 639 struct ip6_tnl_info *ip6_tnl = link->l_info; 640 641 IS_IP6_TNL_LINK_ASSERT(link); 642 643 return ip6_tnl->flags; 644 } 645 646 /** 647 * Set IP6_TNL tunnel proto 648 * @arg link Link object 649 * @arg proto tunnel proto 650 * 651 * @return 0 on success or a negative error code 652 */ 653 int rtnl_link_ip6_tnl_set_proto(struct rtnl_link *link, uint8_t proto) 654 { 655 struct ip6_tnl_info *ip6_tnl = link->l_info; 656 657 IS_IP6_TNL_LINK_ASSERT(link); 658 659 ip6_tnl->proto = proto; 660 ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_PROTO; 661 662 return 0; 663 } 664 665 /** 666 * Get IP6_TNL proto 667 * @arg link Link object 668 * 669 * @return proto value 670 */ 671 uint8_t rtnl_link_ip6_tnl_get_proto(struct rtnl_link *link) 672 { 673 struct ip6_tnl_info *ip6_tnl = link->l_info; 674 675 IS_IP6_TNL_LINK_ASSERT(link); 676 677 return ip6_tnl->proto; 678 } 679 680 static void __init ip6_tnl_init(void) 681 { 682 rtnl_link_register_info(&ip6_tnl_info_ops); 683 } 684 685 static void __exit ip6_tnl_exit(void) 686 { 687 rtnl_link_unregister_info(&ip6_tnl_info_ops); 688 } 689