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