1 /* NHRP netlink/GRE tunnel configuration code
2  * Copyright (c) 2014-2016 Timo Teräs
3  *
4  * This file is free software: you may copy, redistribute and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  */
9 
10 #include <sys/socket.h>
11 #include <linux/netlink.h>
12 #include <linux/rtnetlink.h>
13 #include <linux/in.h>
14 #include <linux/if.h>
15 #include <linux/ip.h>
16 #include <linux/ipv6.h>
17 #include <linux/if_tunnel.h>
18 
19 #include "debug.h"
20 #include "netlink.h"
21 #include "znl.h"
22 
__netlink_gre_get_data(struct zbuf * zb,struct zbuf * data,int ifindex)23 static int __netlink_gre_get_data(struct zbuf *zb, struct zbuf *data, int ifindex)
24 {
25 	struct nlmsghdr *n;
26 	struct ifinfomsg *ifi;
27 	struct zbuf payload, rtapayload;
28 	struct rtattr *rta;
29 
30 	debugf(NHRP_DEBUG_KERNEL, "netlink-link-gre: get-info %u", ifindex);
31 
32 	n = znl_nlmsg_push(zb, RTM_GETLINK, NLM_F_REQUEST);
33 	ifi = znl_push(zb, sizeof(*ifi));
34 	*ifi = (struct ifinfomsg) {
35 		.ifi_index = ifindex,
36 	};
37 	znl_nlmsg_complete(zb, n);
38 
39 	if (zbuf_send(zb, netlink_req_fd) < 0 ||
40 	    zbuf_recv(zb, netlink_req_fd) < 0)
41 		return -1;
42 
43 	n = znl_nlmsg_pull(zb, &payload);
44 	if (!n) return -1;
45 
46 	if (n->nlmsg_type != RTM_NEWLINK)
47 		return -1;
48 
49 	ifi = znl_pull(&payload, sizeof(struct ifinfomsg));
50 	if (!ifi)
51 		return -1;
52 
53 	debugf(NHRP_DEBUG_KERNEL, "netlink-link-gre: ifindex %u, receive msg_type %u, msg_flags %u",
54 		ifi->ifi_index, n->nlmsg_type, n->nlmsg_flags);
55 
56 	if (ifi->ifi_index != ifindex)
57 		return -1;
58 
59 	while ((rta = znl_rta_pull(&payload, &rtapayload)) != NULL)
60 		if (rta->rta_type == IFLA_LINKINFO)
61 			break;
62 	if (!rta) return -1;
63 
64 	payload = rtapayload;
65 	while ((rta = znl_rta_pull(&payload, &rtapayload)) != NULL)
66 		if (rta->rta_type == IFLA_INFO_DATA)
67 			break;
68 	if (!rta) return -1;
69 
70 	*data = rtapayload;
71 	return 0;
72 }
73 
netlink_gre_get_info(unsigned int ifindex,uint32_t * gre_key,unsigned int * link_index,struct in_addr * saddr)74 void netlink_gre_get_info(unsigned int ifindex, uint32_t *gre_key, unsigned int *link_index, struct in_addr *saddr)
75 {
76 	struct zbuf *zb = zbuf_alloc(8192), data, rtapl;
77 	struct rtattr *rta;
78 
79 	*link_index = 0;
80 	*gre_key = 0;
81 	saddr->s_addr = 0;
82 
83 	if (__netlink_gre_get_data(zb, &data, ifindex) < 0)
84 		goto err;
85 
86 	while ((rta = znl_rta_pull(&data, &rtapl)) != NULL) {
87 		switch (rta->rta_type) {
88 		case IFLA_GRE_LINK:
89 			*link_index = zbuf_get32(&rtapl);
90 			break;
91 		case IFLA_GRE_IKEY:
92 		case IFLA_GRE_OKEY:
93 			*gre_key = zbuf_get32(&rtapl);
94 			break;
95 		case IFLA_GRE_LOCAL:
96 			saddr->s_addr = zbuf_get32(&rtapl);
97 			break;
98 		}
99 	}
100 err:
101 	zbuf_free(zb);
102 }
103 
netlink_gre_set_link(unsigned int ifindex,unsigned int link_index)104 void netlink_gre_set_link(unsigned int ifindex, unsigned int link_index)
105 {
106 	struct nlmsghdr *n;
107 	struct ifinfomsg *ifi;
108 	struct rtattr *rta_info, *rta_data, *rta;
109 	struct zbuf *zr = zbuf_alloc(8192), data, rtapl;
110 	struct zbuf *zb = zbuf_alloc(8192);
111 	size_t len;
112 
113 	if (__netlink_gre_get_data(zr, &data, ifindex) < 0)
114 		goto err;
115 
116 	n = znl_nlmsg_push(zb, RTM_NEWLINK, NLM_F_REQUEST);
117 	ifi = znl_push(zb, sizeof(*ifi));
118 	*ifi = (struct ifinfomsg) {
119 		.ifi_index = ifindex,
120 	};
121 	rta_info = znl_rta_nested_push(zb, IFLA_LINKINFO);
122 	znl_rta_push(zb, IFLA_INFO_KIND, "gre", 3);
123 	rta_data = znl_rta_nested_push(zb, IFLA_INFO_DATA);
124 
125 	znl_rta_push_u32(zb, IFLA_GRE_LINK, link_index);
126 	while ((rta = znl_rta_pull(&data, &rtapl)) != NULL) {
127 		if (rta->rta_type == IFLA_GRE_LINK)
128 			continue;
129 		len = zbuf_used(&rtapl);
130 		znl_rta_push(zb, rta->rta_type, zbuf_pulln(&rtapl, len), len);
131 	}
132 
133 	znl_rta_nested_complete(zb, rta_data);
134 	znl_rta_nested_complete(zb, rta_info);
135 
136 	znl_nlmsg_complete(zb, n);
137 	zbuf_send(zb, netlink_req_fd);
138 	zbuf_recv(zb, netlink_req_fd);
139 err:
140 	zbuf_free(zb);
141 	zbuf_free(zr);
142 }
143