New upstream version 1.2.3
[quagga-debian.git] / zebra / irdp_packet.c
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  *
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"
62 #include "checksum.h"
63 #include "sockunion.h"
64 #include "log.h"
65 #include "sockopt.h"
66
67
68 /* GLOBAL VARS */
69
70 int irdp_sock = -1;
71
72 extern struct zebra_t zebrad;
73 extern struct thread *t_irdp_raw;
74
75 static void
76 parse_irdp_packet(char *p, 
77                   int len, 
78                   struct interface *ifp)
79 {
80   struct ip *ip = (struct ip *)p ;
81   struct icmphdr *icmp;
82   struct in_addr src;
83   int ip_hlen, iplen, datalen;
84   struct zebra_if *zi;
85   struct irdp_interface *irdp;
86
87   zi = ifp->info;
88   if (!zi) 
89     return;
90
91   irdp = &zi->irdp;
92   if (!irdp) 
93     return;
94
95   ip_hlen = ip->ip_hl << 2;
96   
97   sockopt_iphdrincl_swab_systoh (ip);
98   
99   iplen = ip->ip_len;
100   datalen = len - ip_hlen;
101   src = ip->ip_src;
102
103   if (len != iplen)
104     {
105       zlog_err ("IRDP: RX length doesn't match IP length");
106       return;
107     }
108
109   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 doesn't receive link-layer, surely? ??? */
117   /* Check so we don't checksum packets longer than oure RX_BUF - (ethlen +
118    len of IP-header) 14+20 */
119   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     }
125
126   icmp = (struct icmphdr *) (p+ip_hlen);
127
128   /* 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   
136   /* Handle just only IRDP */
137   if (!(icmp->type == ICMP_ROUTERADVERT
138         || icmp->type == ICMP_ROUTERSOLICIT))
139     return;
140   
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     }
148
149   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");
161
162       zlog_warn ("IRDP: Please correct settings\n");
163       return;
164     }
165
166   switch (icmp->type) 
167     {
168     case ICMP_ROUTERADVERT:
169       break;
170
171     case ICMP_ROUTERSOLICIT:
172
173       if(irdp->flags & IF_DEBUG_MESSAGES) 
174         zlog_debug ("IRDP: RX Solicit on %s from %s\n",
175                     ifp->name,
176                     inet_ntoa (src));
177
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
188 static int
189 irdp_recvmsg (int sock, u_char *buf, int size, int *ifindex)
190 {
191   struct msghdr msg;
192   struct iovec iov;
193   char adata[CMSG_SPACE( SOPT_SIZE_CMSG_PKTINFO_IPV4() )];
194   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) {
208     zlog_warn("IRDP: recvmsg: read error %s", safe_strerror(errno));
209     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
221   *ifindex = getsockopt_ifindex (AF_INET, &msg);
222
223   return ret;
224 }
225
226 int 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];
232   int ret, ifindex = 0;
233   
234   int irdp_sock = THREAD_FD (r);
235   t_irdp_raw = thread_add_read (zebrad.master, irdp_read_raw, NULL, irdp_sock);
236   
237   ret = irdp_recvmsg (irdp_sock, (u_char *) buf, IRDP_RX_BUF,  &ifindex);
238  
239   if (ret < 0) zlog_warn ("IRDP: RX Error length = %d", ret);
240
241   ifp = if_lookup_by_index(ifindex);
242   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) 
253       zlog_debug("IRDP: RX ICMP for disabled interface %s\n", ifp->name);
254     return 0;
255   }
256
257   if(irdp->flags & IF_DEBUG_PACKET) {
258     int i;
259     zlog_debug("IRDP: RX (idx %d) ", ifindex);
260     for(i=0; i < ret; i++) zlog_debug( "IRDP: RX %x ", buf[i]&0xFF);
261   }
262
263   parse_irdp_packet(buf, ret, ifp);
264
265   return ret;
266 }
267
268 void 
269 send_packet(struct interface *ifp, 
270             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  
287   if (!(ifp->flags & IFF_UP))
288     return;
289
290   if (p)
291     src = ntohl(p->u.prefix4.s_addr);
292   else 
293     src = 0; /* Is filled in */
294   
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;
304   icmp = (struct icmphdr *) (buf + sizeof (struct ip));
305
306   /* Merge IP header with icmp packet */
307   assert (stream_get_endp(s) < (sizeof (buf) - sizeof (struct ip)));
308   stream_get(icmp, s, stream_get_endp(s));
309
310   /* icmp->checksum is already calculated */
311   ip->ip_len  = sizeof(struct ip) + stream_get_endp(s);
312
313   on = 1;
314   if (setsockopt(irdp_sock, IPPROTO_IP, IP_HDRINCL,
315                  (char *) &on, sizeof(on)) < 0)
316     zlog_warn("sendto %s", safe_strerror (errno));
317
318
319   if(dst == INADDR_BROADCAST ) {
320     on = 1;
321     if (setsockopt(irdp_sock, SOL_SOCKET, SO_BROADCAST,
322                    (char *) &on, sizeof(on)) < 0)
323       zlog_warn("sendto %s", safe_strerror (errno));
324   }
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)
330         zlog_warn("sendto %s", safe_strerror (errno));
331   }
332
333   memset(&sockdst,0,sizeof(sockdst));
334   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  
356   sockopt_iphdrincl_swab_htosys (ip);
357   
358   if (sendmsg(irdp_sock, msg, 0) < 0) {
359     zlog_warn("sendto %s", safe_strerror (errno));
360   }
361   /*   printf("TX on %s idx %d\n", ifp->name, ifp->ifindex); */
362 }
363
364
365 #endif /* HAVE_IRDP */
366
367
368