Home | History | Annotate | Download | only in link
      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