]>
Commit | Line | Data |
---|---|---|
2fb975da TT |
1 | /* NHRP netlink/neighbor table arpd 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 | ||
b45ac5f5 DL |
10 | #ifdef HAVE_CONFIG_H |
11 | #include "config.h" | |
12 | #endif | |
13 | ||
2fb975da TT |
14 | #include <fcntl.h> |
15 | #include <net/if.h> | |
16 | #include <netinet/if_ether.h> | |
17 | #include <linux/netlink.h> | |
18 | #include <linux/neighbour.h> | |
19 | #include <linux/netfilter/nfnetlink_log.h> | |
20 | ||
21 | #include "thread.h" | |
b3b75104 PG |
22 | #include "stream.h" |
23 | #include "prefix.h" | |
2fb975da TT |
24 | #include "nhrpd.h" |
25 | #include "netlink.h" | |
26 | #include "znl.h" | |
27 | ||
28 | int netlink_req_fd = -1; | |
29 | int netlink_nflog_group; | |
30 | static int netlink_log_fd = -1; | |
31 | static struct thread *netlink_log_thread; | |
2fb975da TT |
32 | typedef void (*netlink_dispatch_f)(struct nlmsghdr *msg, struct zbuf *zb); |
33 | ||
996c9314 LB |
34 | void netlink_update_binding(struct interface *ifp, union sockunion *proto, |
35 | union sockunion *nbma) | |
2fb975da | 36 | { |
05657ec2 | 37 | nhrp_send_zebra_nbr(proto, nbma, ifp); |
2fb975da TT |
38 | } |
39 | ||
2fb975da TT |
40 | static void netlink_log_register(int fd, int group) |
41 | { | |
42 | struct nlmsghdr *n; | |
43 | struct nfgenmsg *nf; | |
44 | struct nfulnl_msg_config_cmd cmd; | |
45 | struct zbuf *zb = zbuf_alloc(512); | |
46 | ||
996c9314 LB |
47 | n = znl_nlmsg_push(zb, (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG, |
48 | NLM_F_REQUEST | NLM_F_ACK); | |
2fb975da | 49 | nf = znl_push(zb, sizeof(*nf)); |
996c9314 | 50 | *nf = (struct nfgenmsg){ |
2fb975da TT |
51 | .nfgen_family = AF_UNSPEC, |
52 | .version = NFNETLINK_V0, | |
53 | .res_id = htons(group), | |
54 | }; | |
55 | cmd.command = NFULNL_CFG_CMD_BIND; | |
56 | znl_rta_push(zb, NFULA_CFG_CMD, &cmd, sizeof(cmd)); | |
57 | znl_nlmsg_complete(zb, n); | |
58 | ||
59 | zbuf_send(zb, fd); | |
60 | zbuf_free(zb); | |
61 | } | |
62 | ||
63 | static void netlink_log_indication(struct nlmsghdr *msg, struct zbuf *zb) | |
64 | { | |
65 | struct nfgenmsg *nf; | |
66 | struct rtattr *rta; | |
67 | struct zbuf rtapl, pktpl; | |
68 | struct interface *ifp; | |
69 | struct nfulnl_msg_packet_hdr *pkthdr = NULL; | |
70 | uint32_t *in_ndx = NULL; | |
71 | ||
72 | nf = znl_pull(zb, sizeof(*nf)); | |
996c9314 LB |
73 | if (!nf) |
74 | return; | |
2fb975da TT |
75 | |
76 | memset(&pktpl, 0, sizeof(pktpl)); | |
77 | while ((rta = znl_rta_pull(zb, &rtapl)) != NULL) { | |
78 | switch (rta->rta_type) { | |
79 | case NFULA_PACKET_HDR: | |
80 | pkthdr = znl_pull(&rtapl, sizeof(*pkthdr)); | |
81 | break; | |
82 | case NFULA_IFINDEX_INDEV: | |
83 | in_ndx = znl_pull(&rtapl, sizeof(*in_ndx)); | |
84 | break; | |
85 | case NFULA_PAYLOAD: | |
86 | pktpl = rtapl; | |
87 | break; | |
996c9314 LB |
88 | /* NFULA_HWHDR exists and is supposed to contain source |
89 | * hardware address. However, for ip_gre it seems to be | |
90 | * the nexthop destination address if the packet matches | |
91 | * route. */ | |
2fb975da TT |
92 | } |
93 | } | |
94 | ||
95 | if (!pkthdr || !in_ndx || !zbuf_used(&pktpl)) | |
96 | return; | |
97 | ||
7e2b7603 | 98 | ifp = if_lookup_by_index(htonl(*in_ndx), VRF_DEFAULT); |
2fb975da TT |
99 | if (!ifp) |
100 | return; | |
101 | ||
102 | nhrp_peer_send_indication(ifp, htons(pkthdr->hw_protocol), &pktpl); | |
103 | } | |
104 | ||
105 | static int netlink_log_recv(struct thread *t) | |
106 | { | |
107 | uint8_t buf[ZNL_BUFFER_SIZE]; | |
108 | int fd = THREAD_FD(t); | |
109 | struct zbuf payload, zb; | |
110 | struct nlmsghdr *n; | |
111 | ||
112 | netlink_log_thread = NULL; | |
113 | ||
114 | zbuf_init(&zb, buf, sizeof(buf), 0); | |
115 | while (zbuf_recv(&zb, fd) > 0) { | |
831600c3 | 116 | while ((n = znl_nlmsg_pull(&zb, &payload)) != NULL) { |
996c9314 LB |
117 | debugf(NHRP_DEBUG_KERNEL, |
118 | "Netlink-log: Received msg_type %u, msg_flags %u", | |
119 | n->nlmsg_type, n->nlmsg_flags); | |
2fb975da | 120 | switch (n->nlmsg_type) { |
996c9314 | 121 | case (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_PACKET: |
2fb975da TT |
122 | netlink_log_indication(n, &payload); |
123 | break; | |
124 | } | |
125 | } | |
126 | } | |
127 | ||
ffa2c898 QY |
128 | thread_add_read(master, netlink_log_recv, 0, netlink_log_fd, |
129 | &netlink_log_thread); | |
2fb975da TT |
130 | |
131 | return 0; | |
132 | } | |
133 | ||
134 | void netlink_set_nflog_group(int nlgroup) | |
135 | { | |
136 | if (netlink_log_fd >= 0) { | |
50478845 | 137 | thread_cancel(&netlink_log_thread); |
2fb975da TT |
138 | close(netlink_log_fd); |
139 | netlink_log_fd = -1; | |
140 | } | |
141 | netlink_nflog_group = nlgroup; | |
142 | if (nlgroup) { | |
996c9314 | 143 | netlink_log_fd = znl_open(NETLINK_NETFILTER, 0); |
6c8ca260 JB |
144 | if (netlink_log_fd < 0) |
145 | return; | |
146 | ||
2fb975da | 147 | netlink_log_register(netlink_log_fd, nlgroup); |
ffa2c898 QY |
148 | thread_add_read(master, netlink_log_recv, 0, netlink_log_fd, |
149 | &netlink_log_thread); | |
2fb975da TT |
150 | } |
151 | } | |
152 | ||
b3b75104 PG |
153 | void nhrp_neighbor_operation(ZAPI_CALLBACK_ARGS) |
154 | { | |
155 | union sockunion addr = {}, lladdr = {}; | |
156 | struct interface *ifp; | |
157 | ifindex_t idx; | |
158 | struct ethaddr mac; | |
159 | int state, ndm_state; | |
160 | struct nhrp_cache *c; | |
161 | unsigned short l2_len; | |
162 | ||
163 | STREAM_GETL(zclient->ibuf, idx); | |
164 | ifp = if_lookup_by_index(idx, vrf_id); | |
165 | STREAM_GETW(zclient->ibuf, addr.sa.sa_family); | |
166 | if (addr.sa.sa_family == AF_INET) { | |
167 | STREAM_GET(&addr.sin.sin_addr.s_addr, | |
168 | zclient->ibuf, IPV4_MAX_BYTELEN); | |
169 | } else { | |
170 | STREAM_GET(&addr.sin6.sin6_addr.s6_addr, | |
171 | zclient->ibuf, IPV6_MAX_BYTELEN); | |
172 | } | |
173 | STREAM_GETL(zclient->ibuf, ndm_state); | |
174 | ||
175 | STREAM_GETL(zclient->ibuf, l2_len); | |
176 | if (l2_len) { | |
177 | STREAM_GET(&mac, zclient->ibuf, l2_len); | |
178 | if (l2_len == IPV4_MAX_BYTELEN) | |
179 | sockunion_set(&lladdr, AF_INET, (const uint8_t *)&mac, | |
180 | l2_len); | |
181 | } | |
182 | if (!ifp) | |
183 | return; | |
184 | c = nhrp_cache_get(ifp, &addr, 0); | |
185 | if (!c) | |
186 | return; | |
187 | debugf(NHRP_DEBUG_KERNEL, | |
188 | "Netlink: %s %pSU dev %s lladdr %pSU nud 0x%x cache used %u type %u", | |
189 | (cmd == ZEBRA_NHRP_NEIGH_GET) | |
190 | ? "who-has" | |
191 | : (cmd == ZEBRA_NHRP_NEIGH_ADDED) ? "new-neigh" | |
192 | : "del-neigh", | |
193 | &addr, ifp->name, &lladdr, ndm_state, c->used, c->cur.type); | |
194 | if (cmd == ZEBRA_NHRP_NEIGH_GET) { | |
195 | if (c->cur.type >= NHRP_CACHE_CACHED) { | |
196 | nhrp_cache_set_used(c, 1); | |
197 | debugf(NHRP_DEBUG_KERNEL, | |
198 | "Netlink: update binding for %pSU dev %s from c %pSU peer.vc.nbma %pSU to lladdr %pSU", | |
199 | &addr, ifp->name, &c->cur.remote_nbma_natoa, | |
200 | &c->cur.peer->vc->remote.nbma, &lladdr); | |
201 | /* In case of shortcuts, nbma is given by lladdr, not | |
202 | * vc->remote.nbma. | |
203 | */ | |
204 | netlink_update_binding(ifp, &addr, &lladdr); | |
205 | } | |
206 | } else { | |
207 | state = (cmd == ZEBRA_NHRP_NEIGH_ADDED) ? ndm_state | |
208 | : NUD_FAILED; | |
209 | nhrp_cache_set_used(c, state == NUD_REACHABLE); | |
210 | } | |
211 | return; | |
212 | stream_failure: | |
213 | return; | |
214 | } | |
215 | ||
6c8ca260 | 216 | void netlink_init(void) |
2fb975da TT |
217 | { |
218 | netlink_req_fd = znl_open(NETLINK_ROUTE, 0); | |
6c8ca260 JB |
219 | if (netlink_req_fd < 0) |
220 | return; | |
2fb975da | 221 | } |