]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
ca776988 | 2 | /* |
3 | * | |
4 | * Copyright (C) 2000 Robert Olsson. | |
5 | * Swedish University of Agricultural Sciences | |
ca776988 | 6 | */ |
7 | ||
d62a17ae | 8 | /* |
ca776988 | 9 | * This work includes work with the following copywrite: |
10 | * | |
11 | * Copyright (C) 1997, 2000 Kunihiro Ishiguro | |
12 | * | |
13 | */ | |
14 | ||
d62a17ae | 15 | /* |
43e52561 | 16 | * Thanks to Jens Laas at Swedish University of Agricultural Sciences |
ca776988 | 17 | * for reviewing and tests. |
18 | */ | |
19 | ||
20 | ||
21 | #include <zebra.h> | |
43e52561 | 22 | #include <netinet/ip_icmp.h> |
ca776988 | 23 | |
43e52561 | 24 | #include "checksum.h" |
ca776988 | 25 | #include "command.h" |
ca776988 | 26 | #include "connected.h" |
43e52561 QY |
27 | #include "if.h" |
28 | #include "ioctl.h" | |
ca776988 | 29 | #include "log.h" |
43e52561 QY |
30 | #include "log.h" |
31 | #include "memory.h" | |
32 | #include "prefix.h" | |
33 | #include "sockopt.h" | |
34 | #include "sockunion.h" | |
35 | #include "sockunion.h" | |
36 | #include "stream.h" | |
24a58196 | 37 | #include "frrevent.h" |
43e52561 QY |
38 | #include "vty.h" |
39 | #include "zclient.h" | |
9df414fe | 40 | #include "lib_errors.h" |
43e52561 | 41 | |
ca776988 | 42 | #include "zebra/interface.h" |
43 | #include "zebra/rtadv.h" | |
44 | #include "zebra/rib.h" | |
3801e764 | 45 | #include "zebra/zebra_router.h" |
ca776988 | 46 | #include "zebra/redistribute.h" |
47 | #include "zebra/irdp.h" | |
43e52561 | 48 | #include "zebra/zebra_errors.h" |
ca776988 | 49 | |
50 | ||
51 | /* GLOBAL VARS */ | |
52 | ||
2da40f49 | 53 | int irdp_sock = -1; |
ca776988 | 54 | |
e6685141 | 55 | extern struct event *t_irdp_raw; |
ca776988 | 56 | |
d62a17ae | 57 | static void parse_irdp_packet(char *p, int len, struct interface *ifp) |
ca776988 | 58 | { |
d62a17ae | 59 | struct ip *ip = (struct ip *)p; |
60 | struct icmphdr *icmp; | |
61 | struct in_addr src; | |
62 | int ip_hlen, iplen, datalen; | |
63 | struct zebra_if *zi; | |
64 | struct irdp_interface *irdp; | |
30b544ed | 65 | uint16_t saved_chksum; |
9bcef951 | 66 | char buf[PREFIX_STRLEN]; |
d62a17ae | 67 | |
68 | zi = ifp->info; | |
69 | if (!zi) | |
70 | return; | |
71 | ||
ead4ee99 | 72 | irdp = zi->irdp; |
d62a17ae | 73 | if (!irdp) |
74 | return; | |
75 | ||
76 | ip_hlen = ip->ip_hl << 2; | |
77 | ||
78 | sockopt_iphdrincl_swab_systoh(ip); | |
79 | ||
80 | iplen = ip->ip_len; | |
81 | datalen = len - ip_hlen; | |
82 | src = ip->ip_src; | |
83 | ||
84 | if (len != iplen) { | |
e914ccbe | 85 | flog_err(EC_ZEBRA_IRDP_LEN_MISMATCH, |
0437e105 | 86 | "IRDP: RX length doesn't match IP length"); |
d62a17ae | 87 | return; |
88 | } | |
89 | ||
90 | if (iplen < ICMP_MINLEN) { | |
e914ccbe | 91 | flog_err(EC_ZEBRA_IRDP_LEN_MISMATCH, |
1d5453d6 | 92 | "IRDP: RX ICMP packet too short from %pI4", |
9bcef951 | 93 | &src); |
d62a17ae | 94 | return; |
95 | } | |
96 | ||
0437e105 | 97 | /* XXX: RAW doesn't receive link-layer, surely? ??? */ |
d62a17ae | 98 | /* Check so we don't checksum packets longer than oure RX_BUF - (ethlen |
99 | + | |
100 | len of IP-header) 14+20 */ | |
101 | if (iplen > IRDP_RX_BUF - 34) { | |
e914ccbe | 102 | flog_err(EC_ZEBRA_IRDP_LEN_MISMATCH, |
1d5453d6 | 103 | "IRDP: RX ICMP packet too long from %pI4", |
9bcef951 | 104 | &src); |
d62a17ae | 105 | return; |
106 | } | |
107 | ||
108 | icmp = (struct icmphdr *)(p + ip_hlen); | |
109 | ||
30b544ed DS |
110 | saved_chksum = icmp->checksum; |
111 | icmp->checksum = 0; | |
d62a17ae | 112 | /* check icmp checksum */ |
30b544ed | 113 | if (in_cksum(icmp, datalen) != saved_chksum) { |
9df414fe | 114 | flog_warn( |
e914ccbe | 115 | EC_ZEBRA_IRDP_BAD_CHECKSUM, |
9bcef951 MS |
116 | "IRDP: RX ICMP packet from %pI4 Bad checksum, silently ignored", |
117 | &src); | |
d62a17ae | 118 | return; |
119 | } | |
120 | ||
121 | /* Handle just only IRDP */ | |
122 | if (!(icmp->type == ICMP_ROUTERADVERT | |
123 | || icmp->type == ICMP_ROUTERSOLICIT)) | |
124 | return; | |
125 | ||
126 | if (icmp->code != 0) { | |
f203510a QY |
127 | flog_warn( |
128 | EC_ZEBRA_IRDP_BAD_TYPE_CODE, | |
9bcef951 MS |
129 | "IRDP: RX packet type %d from %pI4 Bad ICMP type code, silently ignored", |
130 | icmp->type, &src); | |
d62a17ae | 131 | return; |
132 | } | |
133 | ||
134 | if (!((ntohl(ip->ip_dst.s_addr) == INADDR_BROADCAST) | |
135 | && (irdp->flags & IF_BROADCAST)) | |
136 | || (ntohl(ip->ip_dst.s_addr) == INADDR_ALLRTRS_GROUP | |
137 | && !(irdp->flags & IF_BROADCAST))) { | |
9df414fe | 138 | flog_warn( |
e914ccbe | 139 | EC_ZEBRA_IRDP_BAD_RX_FLAGS, |
1d5453d6 | 140 | "IRDP: RX illegal from %pI4 to %s while %s operates in %s; Please correct settings", |
9bcef951 | 141 | &src, |
d62a17ae | 142 | ntohl(ip->ip_dst.s_addr) == INADDR_ALLRTRS_GROUP |
143 | ? "multicast" | |
9bcef951 MS |
144 | : inet_ntop(AF_INET, &ip->ip_dst, |
145 | buf, sizeof(buf)), | |
d62a17ae | 146 | ifp->name, |
147 | irdp->flags & IF_BROADCAST ? "broadcast" : "multicast"); | |
d62a17ae | 148 | return; |
149 | } | |
150 | ||
151 | switch (icmp->type) { | |
152 | case ICMP_ROUTERADVERT: | |
153 | break; | |
154 | ||
155 | case ICMP_ROUTERSOLICIT: | |
156 | ||
157 | if (irdp->flags & IF_DEBUG_MESSAGES) | |
9bcef951 MS |
158 | zlog_debug("IRDP: RX Solicit on %s from %pI4", |
159 | ifp->name, &src); | |
d62a17ae | 160 | |
161 | process_solicit(ifp); | |
162 | break; | |
163 | ||
164 | default: | |
9df414fe | 165 | flog_warn( |
f203510a | 166 | EC_ZEBRA_IRDP_BAD_TYPE_CODE, |
9bcef951 MS |
167 | "IRDP: RX packet type %d from %pI4 Bad ICMP type code, silently ignored", |
168 | icmp->type, &src); | |
d62a17ae | 169 | } |
ca776988 | 170 | } |
6b0655a2 | 171 | |
d7c0a89a | 172 | static int irdp_recvmsg(int sock, uint8_t *buf, int size, int *ifindex) |
ca776988 | 173 | { |
d62a17ae | 174 | struct msghdr msg; |
175 | struct iovec iov; | |
176 | char adata[CMSG_SPACE(SOPT_SIZE_CMSG_PKTINFO_IPV4())]; | |
177 | int ret; | |
178 | ||
0af35d90 | 179 | memset(&msg, 0, sizeof(msg)); |
d62a17ae | 180 | msg.msg_name = (void *)0; |
181 | msg.msg_namelen = 0; | |
182 | msg.msg_iov = &iov; | |
183 | msg.msg_iovlen = 1; | |
184 | msg.msg_control = (void *)adata; | |
0d6f7fd6 | 185 | msg.msg_controllen = sizeof(adata); |
d62a17ae | 186 | |
187 | iov.iov_base = buf; | |
188 | iov.iov_len = size; | |
189 | ||
190 | ret = recvmsg(sock, &msg, 0); | |
191 | if (ret < 0) { | |
450971aa | 192 | flog_warn(EC_LIB_SOCKET, "IRDP: recvmsg: read error %s", |
9df414fe | 193 | safe_strerror(errno)); |
d62a17ae | 194 | return ret; |
195 | } | |
196 | ||
197 | if (msg.msg_flags & MSG_TRUNC) { | |
450971aa | 198 | flog_warn(EC_LIB_SOCKET, "IRDP: recvmsg: truncated message"); |
d62a17ae | 199 | return ret; |
200 | } | |
201 | if (msg.msg_flags & MSG_CTRUNC) { | |
450971aa | 202 | flog_warn(EC_LIB_SOCKET, |
9df414fe | 203 | "IRDP: recvmsg: truncated control message"); |
d62a17ae | 204 | return ret; |
205 | } | |
206 | ||
207 | *ifindex = getsockopt_ifindex(AF_INET, &msg); | |
208 | ||
209 | return ret; | |
ca776988 | 210 | } |
6b0655a2 | 211 | |
e6685141 | 212 | void irdp_read_raw(struct event *r) |
ca776988 | 213 | { |
d62a17ae | 214 | struct interface *ifp; |
215 | struct zebra_if *zi; | |
216 | struct irdp_interface *irdp; | |
217 | char buf[IRDP_RX_BUF]; | |
218 | int ret, ifindex = 0; | |
219 | ||
e16d030c | 220 | int irdp_sock = EVENT_FD(r); |
907a2395 DS |
221 | event_add_read(zrouter.master, irdp_read_raw, NULL, irdp_sock, |
222 | &t_irdp_raw); | |
d62a17ae | 223 | |
d7c0a89a | 224 | ret = irdp_recvmsg(irdp_sock, (uint8_t *)buf, IRDP_RX_BUF, &ifindex); |
d62a17ae | 225 | |
226 | if (ret < 0) | |
450971aa | 227 | flog_warn(EC_LIB_SOCKET, "IRDP: RX Error length = %d", ret); |
d62a17ae | 228 | |
229 | ifp = if_lookup_by_index(ifindex, VRF_DEFAULT); | |
230 | if (!ifp) | |
cc9f21da | 231 | return; |
d62a17ae | 232 | |
233 | zi = ifp->info; | |
234 | if (!zi) | |
cc9f21da | 235 | return; |
d62a17ae | 236 | |
ead4ee99 | 237 | irdp = zi->irdp; |
d62a17ae | 238 | if (!irdp) |
cc9f21da | 239 | return; |
d62a17ae | 240 | |
241 | if (!(irdp->flags & IF_ACTIVE)) { | |
242 | ||
243 | if (irdp->flags & IF_DEBUG_MISC) | |
9165c5f5 | 244 | zlog_debug("IRDP: RX ICMP for disabled interface %s", |
d62a17ae | 245 | ifp->name); |
cc9f21da | 246 | return; |
d62a17ae | 247 | } |
248 | ||
249 | if (irdp->flags & IF_DEBUG_PACKET) { | |
250 | int i; | |
251 | zlog_debug("IRDP: RX (idx %d) ", ifindex); | |
252 | for (i = 0; i < ret; i++) | |
253 | zlog_debug("IRDP: RX %x ", buf[i] & 0xFF); | |
254 | } | |
255 | ||
256 | parse_irdp_packet(buf, ret, ifp); | |
ca776988 | 257 | } |
6b0655a2 | 258 | |
d7c0a89a QY |
259 | void send_packet(struct interface *ifp, struct stream *s, uint32_t dst, |
260 | struct prefix *p, uint32_t ttl) | |
ca776988 | 261 | { |
d62a17ae | 262 | static struct sockaddr_in sockdst = {AF_INET}; |
263 | struct ip *ip; | |
264 | struct icmphdr *icmp; | |
265 | struct msghdr *msg; | |
266 | struct cmsghdr *cmsg; | |
267 | struct iovec iovector; | |
268 | char msgbuf[256]; | |
269 | char buf[256]; | |
270 | struct in_pktinfo *pktinfo; | |
d7c0a89a QY |
271 | unsigned long src; |
272 | uint8_t on; | |
d62a17ae | 273 | |
274 | if (!(ifp->flags & IFF_UP)) | |
275 | return; | |
276 | ||
277 | if (p) | |
278 | src = ntohl(p->u.prefix4.s_addr); | |
279 | else | |
280 | src = 0; /* Is filled in */ | |
281 | ||
282 | ip = (struct ip *)buf; | |
283 | ip->ip_hl = sizeof(struct ip) >> 2; | |
284 | ip->ip_v = IPVERSION; | |
285 | ip->ip_tos = 0xC0; | |
286 | ip->ip_off = 0L; | |
287 | ip->ip_p = 1; /* IP_ICMP */ | |
288 | ip->ip_ttl = ttl; | |
289 | ip->ip_src.s_addr = src; | |
290 | ip->ip_dst.s_addr = dst; | |
291 | icmp = (struct icmphdr *)(buf + sizeof(struct ip)); | |
292 | ||
293 | /* Merge IP header with icmp packet */ | |
294 | assert(stream_get_endp(s) < (sizeof(buf) - sizeof(struct ip))); | |
295 | stream_get(icmp, s, stream_get_endp(s)); | |
296 | ||
297 | /* icmp->checksum is already calculated */ | |
298 | ip->ip_len = sizeof(struct ip) + stream_get_endp(s); | |
299 | ||
300 | on = 1; | |
301 | if (setsockopt(irdp_sock, IPPROTO_IP, IP_HDRINCL, (char *)&on, | |
302 | sizeof(on)) | |
303 | < 0) | |
3d0deb9c DS |
304 | flog_err(EC_LIB_SOCKET, |
305 | "IRDP: Cannot set IP_HDRINCLU %s(%d) on %s", | |
306 | safe_strerror(errno), errno, ifp->name); | |
d62a17ae | 307 | |
308 | ||
309 | if (dst == INADDR_BROADCAST) { | |
08fa52a8 DS |
310 | uint32_t bon = 1; |
311 | ||
312 | if (setsockopt(irdp_sock, SOL_SOCKET, SO_BROADCAST, &bon, | |
313 | sizeof(bon)) | |
d62a17ae | 314 | < 0) |
3d0deb9c DS |
315 | flog_err(EC_LIB_SOCKET, |
316 | "IRDP: Cannot set SO_BROADCAST %s(%d) on %s", | |
317 | safe_strerror(errno), errno, ifp->name); | |
d62a17ae | 318 | } |
319 | ||
320 | if (dst != INADDR_BROADCAST) | |
321 | setsockopt_ipv4_multicast_loop(irdp_sock, 0); | |
322 | ||
323 | memset(&sockdst, 0, sizeof(sockdst)); | |
324 | sockdst.sin_family = AF_INET; | |
325 | sockdst.sin_addr.s_addr = dst; | |
326 | ||
327 | cmsg = (struct cmsghdr *)(msgbuf + sizeof(struct msghdr)); | |
328 | cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(struct in_pktinfo); | |
329 | cmsg->cmsg_level = SOL_IP; | |
330 | cmsg->cmsg_type = IP_PKTINFO; | |
331 | pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); | |
332 | pktinfo->ipi_ifindex = ifp->ifindex; | |
333 | pktinfo->ipi_spec_dst.s_addr = src; | |
334 | pktinfo->ipi_addr.s_addr = src; | |
335 | ||
336 | iovector.iov_base = (void *)buf; | |
337 | iovector.iov_len = ip->ip_len; | |
338 | msg = (struct msghdr *)msgbuf; | |
339 | msg->msg_name = &sockdst; | |
340 | msg->msg_namelen = sizeof(sockdst); | |
341 | msg->msg_iov = &iovector; | |
342 | msg->msg_iovlen = 1; | |
343 | msg->msg_control = cmsg; | |
344 | msg->msg_controllen = cmsg->cmsg_len; | |
345 | ||
346 | sockopt_iphdrincl_swab_htosys(ip); | |
347 | ||
3d0deb9c DS |
348 | if (sendmsg(irdp_sock, msg, 0) < 0) |
349 | flog_err(EC_LIB_SOCKET, | |
350 | "IRDP: sendmsg send failure %s(%d) on %s", | |
351 | safe_strerror(errno), errno, ifp->name); | |
ca776988 | 352 | } |