blob: af6bb80b573205ef862023f6bfc5ab7fcb14fff0 [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#ifdef HAVE_IRDP
40
41#include "if.h"
42#include "vty.h"
43#include "sockunion.h"
hassoc9e52be2004-09-26 16:09:34 +000044#include "sockopt.h"
hassoca776982004-06-12 14:33:05 +000045#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"
hasso25dac852004-07-13 03:06:51 +000054#include "privs.h"
hassoca776982004-06-12 14:33:05 +000055#include "zebra/interface.h"
56#include "zebra/rtadv.h"
57#include "zebra/rib.h"
58#include "zebra/zserv.h"
59#include "zebra/redistribute.h"
60#include "zebra/irdp.h"
61#include <netinet/ip_icmp.h>
62
63#include "if.h"
64#include "sockunion.h"
65#include "log.h"
66
67/* GLOBAL VARS */
68
hasso25dac852004-07-13 03:06:51 +000069extern struct zebra_privs_t zserv_privs;
70
hassoca776982004-06-12 14:33:05 +000071/* Master of threads. */
72extern struct zebra_t zebrad;
73struct thread *t_irdp_raw;
74
75/* Timer interval of irdp. */
76int irdp_timer_interval = IRDP_DEFAULT_INTERVAL;
77
78int irdp_read_raw(struct thread *r);
79int in_cksum (void *ptr, int nbytes);
hassoca776982004-06-12 14:33:05 +000080void send_packet(struct interface *ifp,
81 struct stream *s,
82 u_int32_t dst,
83 struct prefix *p,
84 u_int32_t ttl);
85
hassoca776982004-06-12 14:33:05 +000086char *
87inet_2a(u_int32_t a, char *b)
88{
89 sprintf(b, "%u.%u.%u.%u",
90 (a ) & 0xFF,
91 (a>> 8) & 0xFF,
92 (a>>16) & 0xFF,
93 (a>>24) & 0xFF);
94 return b;
95}
96
97int
98irdp_sock_init (void)
99{
100 int ret, i;
ajs4460e7a2005-01-29 17:07:40 +0000101 int save_errno;
ajs2da40f42005-03-30 16:33:13 +0000102 int sock;
hassoca776982004-06-12 14:33:05 +0000103
hasso25dac852004-07-13 03:06:51 +0000104 if ( zserv_privs.change (ZPRIVS_RAISE) )
105 zlog_err ("irdp_sock_init: could not raise privs, %s",
ajs6099b3b2004-11-20 02:06:59 +0000106 safe_strerror (errno) );
hasso25dac852004-07-13 03:06:51 +0000107
ajs2da40f42005-03-30 16:33:13 +0000108 sock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
ajs4460e7a2005-01-29 17:07:40 +0000109 save_errno = errno;
hasso25dac852004-07-13 03:06:51 +0000110
111 if ( zserv_privs.change (ZPRIVS_LOWER) )
112 zlog_err ("irdp_sock_init: could not lower privs, %s",
ajs6099b3b2004-11-20 02:06:59 +0000113 safe_strerror (errno) );
hasso25dac852004-07-13 03:06:51 +0000114
ajs2da40f42005-03-30 16:33:13 +0000115 if (sock < 0) {
ajs4460e7a2005-01-29 17:07:40 +0000116 zlog_warn ("IRDP: can't create irdp socket %s", safe_strerror(save_errno));
ajs2da40f42005-03-30 16:33:13 +0000117 return sock;
hassoca776982004-06-12 14:33:05 +0000118 };
119
120 i = 1;
ajs2da40f42005-03-30 16:33:13 +0000121 ret = setsockopt (sock, IPPROTO_IP, IP_TTL,
hassoca776982004-06-12 14:33:05 +0000122 (void *) &i, sizeof (i));
123 if (ret < 0) {
ajs6099b3b2004-11-20 02:06:59 +0000124 zlog_warn ("IRDP: can't do irdp sockopt %s", safe_strerror(errno));
ajs2da40f42005-03-30 16:33:13 +0000125 close(sock);
hassoca776982004-06-12 14:33:05 +0000126 return ret;
127 };
128
ajs2da40f42005-03-30 16:33:13 +0000129 ret = setsockopt_ifindex (AF_INET, sock, 1);
hassoca776982004-06-12 14:33:05 +0000130 if (ret < 0) {
ajs6099b3b2004-11-20 02:06:59 +0000131 zlog_warn ("IRDP: can't do irdp sockopt %s", safe_strerror(errno));
ajs2da40f42005-03-30 16:33:13 +0000132 close(sock);
hassoca776982004-06-12 14:33:05 +0000133 return ret;
134 };
135
ajs2da40f42005-03-30 16:33:13 +0000136 t_irdp_raw = thread_add_read (zebrad.master, irdp_read_raw, NULL, sock);
hassoca776982004-06-12 14:33:05 +0000137
ajs2da40f42005-03-30 16:33:13 +0000138 return sock;
hassoca776982004-06-12 14:33:05 +0000139}
140
141
ajs2da40f42005-03-30 16:33:13 +0000142static int
143get_pref(struct irdp_interface *irdp, struct prefix *p)
hassoca776982004-06-12 14:33:05 +0000144{
paul0c0f9112004-09-24 08:24:42 +0000145 struct listnode *node;
hassoca776982004-06-12 14:33:05 +0000146 struct Adv *adv;
147
148 /* Use default preference or use the override pref */
149
paul0c0f9112004-09-24 08:24:42 +0000150 if( irdp->AdvPrefList == NULL )
151 return irdp->Preference;
hassoca776982004-06-12 14:33:05 +0000152
paul0c0f9112004-09-24 08:24:42 +0000153 LIST_LOOP (irdp->AdvPrefList, adv, node)
hassoca776982004-06-12 14:33:05 +0000154 if( p->u.prefix4.s_addr == adv->ip.s_addr )
155 return adv->pref;
paul0c0f9112004-09-24 08:24:42 +0000156
hassoca776982004-06-12 14:33:05 +0000157 return irdp->Preference;
158}
159
160/* Make ICMP Router Advertisement Message. */
ajs2da40f42005-03-30 16:33:13 +0000161static int
162make_advertisement_packet (struct interface *ifp,
163 struct prefix *p,
164 struct stream *s)
hassoca776982004-06-12 14:33:05 +0000165{
166 struct zebra_if *zi=ifp->info;
167 struct irdp_interface *irdp=&zi->irdp;
168 int size;
169 int pref;
170 u_int16_t checksum;
171
172 pref = get_pref(irdp, p);
173
174 stream_putc (s, ICMP_ROUTERADVERT); /* Type. */
175 stream_putc (s, 0); /* Code. */
176 stream_putw (s, 0); /* Checksum. */
177 stream_putc (s, 1); /* Num address. */
178 stream_putc (s, 2); /* Address Entry Size. */
179
180 if(irdp->flags & IF_SHUTDOWN)
181 stream_putw (s, 0);
182 else
183 stream_putw (s, irdp->Lifetime);
184
185 stream_putl (s, htonl(p->u.prefix4.s_addr)); /* Router address. */
186 stream_putl (s, pref);
187
188 /* in_cksum return network byte order value */
189 size = 16;
190 checksum = in_cksum (s->data, size);
191 stream_putw_at (s, 2, htons(checksum));
192
193 return size;
194}
195
ajs2da40f42005-03-30 16:33:13 +0000196static void
197irdp_send(struct interface *ifp, struct prefix *p, struct stream *s)
hassoca776982004-06-12 14:33:05 +0000198{
199 struct zebra_if *zi=ifp->info;
200 struct irdp_interface *irdp=&zi->irdp;
201 u_int32_t dst;
202 u_int32_t ttl=1;
203
204 if (! (ifp->flags & IFF_UP)) return;
205
206 if (irdp->flags & IF_BROADCAST)
207 dst =INADDR_BROADCAST ;
208 else
209 dst = htonl(INADDR_ALLHOSTS_GROUP);
210
211 if(irdp->flags & IF_DEBUG_MESSAGES)
ajsb6178002004-12-07 21:12:56 +0000212 zlog_debug("IRDP: TX Advert on %s %s/%d Holdtime=%d Preference=%d",
hassoca776982004-06-12 14:33:05 +0000213 ifp->name,
214 inet_ntoa(p->u.prefix4),
215 p->prefixlen,
216 irdp->flags & IF_SHUTDOWN? 0 : irdp->Lifetime,
217 get_pref(irdp, p));
218
219 send_packet (ifp, s, dst, p, ttl);
220}
221
ajs2da40f42005-03-30 16:33:13 +0000222static void irdp_advertisement (struct interface *ifp, struct prefix *p)
hassoca776982004-06-12 14:33:05 +0000223{
224 struct stream *s;
225 s = stream_new (128);
226 make_advertisement_packet (ifp, p, s);
227 irdp_send(ifp, p, s);
228}
229
230int irdp_send_thread(struct thread *t_advert)
231{
232 u_int32_t timer, tmp;
233 struct interface *ifp = THREAD_ARG (t_advert);
234 struct zebra_if *zi=ifp->info;
235 struct irdp_interface *irdp=&zi->irdp;
236 struct prefix *p;
paul0c0f9112004-09-24 08:24:42 +0000237 struct listnode *node;
hassoca776982004-06-12 14:33:05 +0000238 struct connected *ifc;
239
240 irdp->flags &= ~IF_SOLICIT;
241
242 if(ifp->connected)
paul0c0f9112004-09-24 08:24:42 +0000243 LIST_LOOP (ifp->connected, ifc, node)
244 {
245 p = ifc->address;
246 irdp_advertisement(ifp, p);
247 irdp->irdp_sent++;
248 }
hassoca776982004-06-12 14:33:05 +0000249
250 tmp = irdp->MaxAdvertInterval-irdp->MinAdvertInterval;
251 timer = (random () % tmp ) + 1;
252 timer = irdp->MinAdvertInterval + timer;
253
254 if(irdp->irdp_sent < MAX_INITIAL_ADVERTISEMENTS &&
255 timer > MAX_INITIAL_ADVERT_INTERVAL )
256 timer= MAX_INITIAL_ADVERT_INTERVAL;
257
258 if(irdp->flags & IF_DEBUG_MISC)
ajsb6178002004-12-07 21:12:56 +0000259 zlog_debug("IRDP: New timer for %s set to %u\n", ifp->name, timer);
hassoca776982004-06-12 14:33:05 +0000260
261 irdp->t_advertise = thread_add_timer(zebrad.master, irdp_send_thread, ifp, timer);
262 return 0;
263}
264
265void irdp_advert_off(struct interface *ifp)
266{
267 struct zebra_if *zi=ifp->info;
268 struct irdp_interface *irdp=&zi->irdp;
paul0c0f9112004-09-24 08:24:42 +0000269 struct listnode *node;
hassoca776982004-06-12 14:33:05 +0000270 int i;
271 struct connected *ifc;
272 struct prefix *p;
273
274 if(irdp->t_advertise) thread_cancel(irdp->t_advertise);
275 irdp->t_advertise = NULL;
276
277 if(ifp->connected)
paul0c0f9112004-09-24 08:24:42 +0000278 LIST_LOOP (ifp->connected, ifc, node)
279 {
280 p = ifc->address;
hassoca776982004-06-12 14:33:05 +0000281
paul0c0f9112004-09-24 08:24:42 +0000282 /* Output some packets with Lifetime 0
283 we should add a wait...
284 */
hassoca776982004-06-12 14:33:05 +0000285
paul0c0f9112004-09-24 08:24:42 +0000286 for(i=0; i< IRDP_LAST_ADVERT_MESSAGES; i++)
287 {
288 irdp->irdp_sent++;
289 irdp_advertisement(ifp, p);
290 }
hassoca776982004-06-12 14:33:05 +0000291 }
hassoca776982004-06-12 14:33:05 +0000292}
293
294
295void process_solicit (struct interface *ifp)
296{
297 struct zebra_if *zi=ifp->info;
298 struct irdp_interface *irdp=&zi->irdp;
299 u_int32_t timer;
300
301 /* When SOLICIT is active we reject further incoming solicits
302 this keeps down the answering rate so we don't have think
303 about DoS attacks here. */
304
305 if( irdp->flags & IF_SOLICIT) return;
306
307 irdp->flags |= IF_SOLICIT;
308 if(irdp->t_advertise) thread_cancel(irdp->t_advertise);
309 irdp->t_advertise = NULL;
310
311 timer = (random () % MAX_RESPONSE_DELAY) + 1;
312
313 irdp->t_advertise = thread_add_timer(zebrad.master,
314 irdp_send_thread,
315 ifp,
316 timer);
317}
318
319void irdp_finish()
320{
321
paul0c0f9112004-09-24 08:24:42 +0000322 struct listnode *node;
hassoca776982004-06-12 14:33:05 +0000323 struct interface *ifp;
324 struct zebra_if *zi;
325 struct irdp_interface *irdp;
326
ajs274a4a42004-12-07 15:39:31 +0000327 zlog_info("IRDP: Received shutdown notification.");
hassoca776982004-06-12 14:33:05 +0000328
329 for (node = listhead (iflist); node; node = nextnode (node))
330 {
331 ifp = getdata(node);
paul0c0f9112004-09-24 08:24:42 +0000332 zi = ifp->info;
333
334 if (!zi)
335 continue;
hassoca776982004-06-12 14:33:05 +0000336 irdp = &zi->irdp;
paul0c0f9112004-09-24 08:24:42 +0000337 if (!irdp)
338 continue;
hassoca776982004-06-12 14:33:05 +0000339
paul0c0f9112004-09-24 08:24:42 +0000340 if (irdp->flags & IF_ACTIVE )
341 {
342 irdp->flags |= IF_SHUTDOWN;
343 irdp_advert_off(ifp);
344 }
hassoca776982004-06-12 14:33:05 +0000345 }
346}
347
hassoca776982004-06-12 14:33:05 +0000348#endif /* HAVE_IRDP */