blob: 50525043d30ca05df1fd1ce2391cf8a738843693 [file] [log] [blame]
hassoca776982004-06-12 14:33:05 +00001/*
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 *
18 * You should have received a copy of the GNU General Public License
19 * along with GNU Zebra; see the file COPYING. If not, write to the Free
20 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21 * 02111-1307, USA.
22 */
23
24/*
25 * This work includes work with the following copywrite:
26 *
27 * Copyright (C) 1997, 2000 Kunihiro Ishiguro
28 *
29 */
30
31/*
32 * Thanks to Jens Låås at Swedish University of Agricultural Sciences
33 * for reviewing and tests.
34 */
35
36
37#include <zebra.h>
38
39
40#ifdef HAVE_IRDP
41
42#include "if.h"
43#include "vty.h"
44#include "sockunion.h"
45#include "prefix.h"
46#include "command.h"
47#include "memory.h"
48#include "stream.h"
49#include "ioctl.h"
50#include "connected.h"
51#include "log.h"
52#include "zclient.h"
53#include "thread.h"
54#include "zebra/interface.h"
55#include "zebra/rtadv.h"
56#include "zebra/rib.h"
57#include "zebra/zserv.h"
58#include "zebra/redistribute.h"
59#include "zebra/irdp.h"
60#include <netinet/ip_icmp.h>
61#include "if.h"
Stephen Hemmingerab0f6152009-12-10 14:22:44 +030062#include "checksum.h"
hassoca776982004-06-12 14:33:05 +000063#include "sockunion.h"
64#include "log.h"
paul0de1cde2004-08-19 04:45:33 +000065#include "sockopt.h"
hassoca776982004-06-12 14:33:05 +000066
67
68/* GLOBAL VARS */
69
ajs2da40f42005-03-30 16:33:13 +000070int irdp_sock = -1;
hassoca776982004-06-12 14:33:05 +000071
72extern struct zebra_t zebrad;
73extern struct thread *t_irdp_raw;
hassoca776982004-06-12 14:33:05 +000074
ajs2da40f42005-03-30 16:33:13 +000075static void
76parse_irdp_packet(char *p,
hassoca776982004-06-12 14:33:05 +000077 int len,
78 struct interface *ifp)
79{
80 struct ip *ip = (struct ip *)p ;
81 struct icmphdr *icmp;
82 struct in_addr src;
paul72164662004-10-05 14:39:43 +000083 int ip_hlen, iplen, datalen;
hassoca776982004-06-12 14:33:05 +000084 struct zebra_if *zi;
85 struct irdp_interface *irdp;
86
87 zi = ifp->info;
paul72164662004-10-05 14:39:43 +000088 if (!zi)
89 return;
hassoca776982004-06-12 14:33:05 +000090
91 irdp = &zi->irdp;
paul72164662004-10-05 14:39:43 +000092 if (!irdp)
93 return;
hassoca776982004-06-12 14:33:05 +000094
paul72164662004-10-05 14:39:43 +000095 ip_hlen = ip->ip_hl << 2;
96
97 sockopt_iphdrincl_swab_systoh (ip);
98
99 iplen = ip->ip_len;
100 datalen = len - ip_hlen;
hassoca776982004-06-12 14:33:05 +0000101 src = ip->ip_src;
102
paul72164662004-10-05 14:39:43 +0000103 if (len != iplen)
104 {
105 zlog_err ("IRDP: RX length doesnt match IP length");
106 return;
107 }
hassoca776982004-06-12 14:33:05 +0000108
paul72164662004-10-05 14:39:43 +0000109 if (iplen < ICMP_MINLEN)
110 {
111 zlog_err ("IRDP: RX ICMP packet too short from %s\n",
112 inet_ntoa (src));
113 return;
114 }
115
116 /* XXX: RAW doesnt receive link-layer, surely? ??? */
hassoca776982004-06-12 14:33:05 +0000117 /* Check so we don't checksum packets longer than oure RX_BUF - (ethlen +
118 len of IP-header) 14+20 */
paul72164662004-10-05 14:39:43 +0000119 if (iplen > IRDP_RX_BUF-34)
120 {
121 zlog_err ("IRDP: RX ICMP packet too long from %s\n",
122 inet_ntoa (src));
123 return;
124 }
hassoca776982004-06-12 14:33:05 +0000125
126 icmp = (struct icmphdr *) (p+ip_hlen);
127
paul72164662004-10-05 14:39:43 +0000128 /* check icmp checksum */
129 if (in_cksum (icmp, datalen) != icmp->checksum)
130 {
131 zlog_warn ("IRDP: RX ICMP packet from %s. Bad checksum, silently ignored",
132 inet_ntoa (src));
133 return;
134 }
135
hassoca776982004-06-12 14:33:05 +0000136 /* Handle just only IRDP */
paul72164662004-10-05 14:39:43 +0000137 if (!(icmp->type == ICMP_ROUTERADVERT
138 || icmp->type == ICMP_ROUTERSOLICIT))
hassoca776982004-06-12 14:33:05 +0000139 return;
paul72164662004-10-05 14:39:43 +0000140
141 if (icmp->code != 0)
142 {
143 zlog_warn ("IRDP: RX packet type %d from %s. Bad ICMP type code,"
144 " silently ignored",
145 icmp->type, inet_ntoa (src));
146 return;
147 }
hassoca776982004-06-12 14:33:05 +0000148
paul72164662004-10-05 14:39:43 +0000149 if (! ((ntohl (ip->ip_dst.s_addr) == INADDR_BROADCAST)
150 && (irdp->flags & IF_BROADCAST))
151 ||
152 (ntohl (ip->ip_dst.s_addr) == INADDR_ALLRTRS_GROUP
153 && !(irdp->flags & IF_BROADCAST)))
154 {
155 zlog_warn ("IRDP: RX illegal from %s to %s while %s operates in %s\n",
156 inet_ntoa (src),
157 ntohl (ip->ip_dst.s_addr) == INADDR_ALLRTRS_GROUP ?
158 "multicast" : inet_ntoa (ip->ip_dst),
159 ifp->name,
160 irdp->flags & IF_BROADCAST ? "broadcast" : "multicast");
hassoca776982004-06-12 14:33:05 +0000161
paul72164662004-10-05 14:39:43 +0000162 zlog_warn ("IRDP: Please correct settings\n");
163 return;
164 }
hassoca776982004-06-12 14:33:05 +0000165
166 switch (icmp->type)
167 {
168 case ICMP_ROUTERADVERT:
169 break;
170
171 case ICMP_ROUTERSOLICIT:
172
173 if(irdp->flags & IF_DEBUG_MESSAGES)
ajsb6178002004-12-07 21:12:56 +0000174 zlog_debug ("IRDP: RX Solicit on %s from %s\n",
175 ifp->name,
176 inet_ntoa (src));
hassoca776982004-06-12 14:33:05 +0000177
178 process_solicit(ifp);
179 break;
180
181 default:
182 zlog_warn ("IRDP: RX type %d from %s. Bad ICMP type, silently ignored",
183 icmp->type,
184 inet_ntoa (src));
185 }
186}
187
ajs2da40f42005-03-30 16:33:13 +0000188static int
189irdp_recvmsg (int sock, u_char *buf, int size, int *ifindex)
hassoca776982004-06-12 14:33:05 +0000190{
191 struct msghdr msg;
192 struct iovec iov;
paul1470baf2004-07-23 15:25:01 +0000193 char adata[CMSG_SPACE( SOPT_SIZE_CMSG_PKTINFO_IPV4() )];
hassoca776982004-06-12 14:33:05 +0000194 int ret;
195
196 msg.msg_name = (void *)0;
197 msg.msg_namelen = 0;
198 msg.msg_iov = &iov;
199 msg.msg_iovlen = 1;
200 msg.msg_control = (void *) adata;
201 msg.msg_controllen = sizeof adata;
202
203 iov.iov_base = buf;
204 iov.iov_len = size;
205
206 ret = recvmsg (sock, &msg, 0);
207 if (ret < 0) {
ajs6099b3b2004-11-20 02:06:59 +0000208 zlog_warn("IRDP: recvmsg: read error %s", safe_strerror(errno));
hassoca776982004-06-12 14:33:05 +0000209 return ret;
210 }
211
212 if (msg.msg_flags & MSG_TRUNC) {
213 zlog_warn("IRDP: recvmsg: truncated message");
214 return ret;
215 }
216 if (msg.msg_flags & MSG_CTRUNC) {
217 zlog_warn("IRDP: recvmsg: truncated control message");
218 return ret;
219 }
220
paul0c0f9112004-09-24 08:24:42 +0000221 *ifindex = getsockopt_ifindex (AF_INET, &msg);
paul1470baf2004-07-23 15:25:01 +0000222
hassoca776982004-06-12 14:33:05 +0000223 return ret;
224}
225
226int irdp_read_raw(struct thread *r)
227{
228 struct interface *ifp;
229 struct zebra_if *zi;
230 struct irdp_interface *irdp;
231 char buf[IRDP_RX_BUF];
Stephen Hemmingerab0f6152009-12-10 14:22:44 +0300232 int ret, ifindex = 0;
hassoca776982004-06-12 14:33:05 +0000233
234 int irdp_sock = THREAD_FD (r);
235 t_irdp_raw = thread_add_read (zebrad.master, irdp_read_raw, NULL, irdp_sock);
236
hassoc9e52be2004-09-26 16:09:34 +0000237 ret = irdp_recvmsg (irdp_sock, (u_char *) buf, IRDP_RX_BUF, &ifindex);
hassoca776982004-06-12 14:33:05 +0000238
239 if (ret < 0) zlog_warn ("IRDP: RX Error length = %d", ret);
240
ajs2da40f42005-03-30 16:33:13 +0000241 ifp = if_lookup_by_index(ifindex);
hassoca776982004-06-12 14:33:05 +0000242 if(! ifp ) return ret;
243
244 zi= ifp->info;
245 if(! zi ) return ret;
246
247 irdp = &zi->irdp;
248 if(! irdp ) return ret;
249
250 if(! (irdp->flags & IF_ACTIVE)) {
251
252 if(irdp->flags & IF_DEBUG_MISC)
ajsb6178002004-12-07 21:12:56 +0000253 zlog_debug("IRDP: RX ICMP for disabled interface %s\n", ifp->name);
hassoca776982004-06-12 14:33:05 +0000254 return 0;
255 }
256
257 if(irdp->flags & IF_DEBUG_PACKET) {
258 int i;
ajsb6178002004-12-07 21:12:56 +0000259 zlog_debug("IRDP: RX (idx %d) ", ifindex);
260 for(i=0; i < ret; i++) zlog_debug( "IRDP: RX %x ", buf[i]&0xFF);
hassoca776982004-06-12 14:33:05 +0000261 }
262
263 parse_irdp_packet(buf, ret, ifp);
paul72164662004-10-05 14:39:43 +0000264
hassoca776982004-06-12 14:33:05 +0000265 return ret;
266}
267
paul72164662004-10-05 14:39:43 +0000268void
269send_packet(struct interface *ifp,
hassoca776982004-06-12 14:33:05 +0000270 struct stream *s,
271 u_int32_t dst,
272 struct prefix *p,
273 u_int32_t ttl)
274{
275 static struct sockaddr_in sockdst = {AF_INET};
276 struct ip *ip;
277 struct icmphdr *icmp;
278 struct msghdr *msg;
279 struct cmsghdr *cmsg;
280 struct iovec iovector;
281 char msgbuf[256];
282 char buf[256];
283 struct in_pktinfo *pktinfo;
284 u_long src;
285 int on;
286
Paul Jakma36943742006-08-04 06:18:04 +0000287 if (!(ifp->flags & IFF_UP))
paul72164662004-10-05 14:39:43 +0000288 return;
hassoca776982004-06-12 14:33:05 +0000289
Remi Gacognea11e0122013-09-08 13:48:34 +0000290 if (p)
paul72164662004-10-05 14:39:43 +0000291 src = ntohl(p->u.prefix4.s_addr);
292 else
293 src = 0; /* Is filled in */
hassoca776982004-06-12 14:33:05 +0000294
295 ip = (struct ip *) buf;
296 ip->ip_hl = sizeof(struct ip) >> 2;
297 ip->ip_v = IPVERSION;
298 ip->ip_tos = 0xC0;
299 ip->ip_off = 0L;
300 ip->ip_p = 1; /* IP_ICMP */
301 ip->ip_ttl = ttl;
302 ip->ip_src.s_addr = src;
303 ip->ip_dst.s_addr = dst;
paul72164662004-10-05 14:39:43 +0000304 icmp = (struct icmphdr *) (buf + sizeof (struct ip));
hassoca776982004-06-12 14:33:05 +0000305
306 /* Merge IP header with icmp packet */
paul9985f832005-02-09 15:51:56 +0000307 assert (stream_get_endp(s) < (sizeof (buf) - sizeof (struct ip)));
308 stream_get(icmp, s, stream_get_endp(s));
hassoca776982004-06-12 14:33:05 +0000309
310 /* icmp->checksum is already calculated */
paul9985f832005-02-09 15:51:56 +0000311 ip->ip_len = sizeof(struct ip) + stream_get_endp(s);
hassoca776982004-06-12 14:33:05 +0000312
313 on = 1;
314 if (setsockopt(irdp_sock, IPPROTO_IP, IP_HDRINCL,
315 (char *) &on, sizeof(on)) < 0)
ajs6099b3b2004-11-20 02:06:59 +0000316 zlog_warn("sendto %s", safe_strerror (errno));
hassoca776982004-06-12 14:33:05 +0000317
318
319 if(dst == INADDR_BROADCAST ) {
320 on = 1;
321 if (setsockopt(irdp_sock, SOL_SOCKET, SO_BROADCAST,
322 (char *) &on, sizeof(on)) < 0)
ajs6099b3b2004-11-20 02:06:59 +0000323 zlog_warn("sendto %s", safe_strerror (errno));
hassoca776982004-06-12 14:33:05 +0000324 }
325
326 if(dst != INADDR_BROADCAST) {
327 on = 0;
328 if( setsockopt(irdp_sock,IPPROTO_IP, IP_MULTICAST_LOOP,
329 (char *)&on,sizeof(on)) < 0)
ajs6099b3b2004-11-20 02:06:59 +0000330 zlog_warn("sendto %s", safe_strerror (errno));
hassoca776982004-06-12 14:33:05 +0000331 }
332
Jeremy Jacksonec5e42b2009-01-21 20:51:57 -0500333 memset(&sockdst,0,sizeof(sockdst));
hassoca776982004-06-12 14:33:05 +0000334 sockdst.sin_family=AF_INET;
335 sockdst.sin_addr.s_addr = dst;
336
337 cmsg = (struct cmsghdr *) (msgbuf + sizeof(struct msghdr));
338 cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(struct in_pktinfo);
339 cmsg->cmsg_level = SOL_IP;
340 cmsg->cmsg_type = IP_PKTINFO;
341 pktinfo = (struct in_pktinfo *) CMSG_DATA(cmsg);
342 pktinfo->ipi_ifindex = ifp->ifindex;
343 pktinfo->ipi_spec_dst.s_addr = src;
344 pktinfo->ipi_addr.s_addr = src;
345
346 iovector.iov_base = (void *) buf;
347 iovector.iov_len = ip->ip_len;
348 msg = (struct msghdr *) msgbuf;
349 msg->msg_name = &sockdst;
350 msg->msg_namelen = sizeof(sockdst);
351 msg->msg_iov = &iovector;
352 msg->msg_iovlen = 1;
353 msg->msg_control = cmsg;
354 msg->msg_controllen = cmsg->cmsg_len;
355
paul72164662004-10-05 14:39:43 +0000356 sockopt_iphdrincl_swab_htosys (ip);
357
hassoca776982004-06-12 14:33:05 +0000358 if (sendmsg(irdp_sock, msg, 0) < 0) {
ajs6099b3b2004-11-20 02:06:59 +0000359 zlog_warn("sendto %s", safe_strerror (errno));
hassoca776982004-06-12 14:33:05 +0000360 }
361 /* printf("TX on %s idx %d\n", ifp->name, ifp->ifindex); */
362}
363
364
365#endif /* HAVE_IRDP */
366
367
368