3 * Copyright (C) 2000 Robert Olsson.
4 * Swedish University of Agricultural Sciences
6 * This file is part of GNU Zebra.
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
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.
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
24 * This work includes work with the following copywrite:
26 * Copyright (C) 1997, 2000 Kunihiro Ishiguro
31 * Thanks to Jens Laas at Swedish University of Agricultural Sciences
32 * for reviewing and tests.
37 #include <netinet/ip_icmp.h>
41 #include "connected.h"
49 #include "sockunion.h"
50 #include "sockunion.h"
55 #include "lib_errors.h"
57 #include "zebra_memory.h"
58 #include "zebra/interface.h"
59 #include "zebra/rtadv.h"
60 #include "zebra/rib.h"
61 #include "zebra/zebra_router.h"
62 #include "zebra/redistribute.h"
63 #include "zebra/irdp.h"
64 #include "zebra/zebra_errors.h"
71 extern struct thread
*t_irdp_raw
;
73 static void parse_irdp_packet(char *p
, int len
, struct interface
*ifp
)
75 struct ip
*ip
= (struct ip
*)p
;
78 int ip_hlen
, iplen
, datalen
;
80 struct irdp_interface
*irdp
;
81 uint16_t saved_chksum
;
91 ip_hlen
= ip
->ip_hl
<< 2;
93 sockopt_iphdrincl_swab_systoh(ip
);
96 datalen
= len
- ip_hlen
;
100 flog_err(EC_ZEBRA_IRDP_LEN_MISMATCH
,
101 "IRDP: RX length doesn't match IP length");
105 if (iplen
< ICMP_MINLEN
) {
106 flog_err(EC_ZEBRA_IRDP_LEN_MISMATCH
,
107 "IRDP: RX ICMP packet too short from %s\n",
112 /* XXX: RAW doesn't receive link-layer, surely? ??? */
113 /* Check so we don't checksum packets longer than oure RX_BUF - (ethlen
115 len of IP-header) 14+20 */
116 if (iplen
> IRDP_RX_BUF
- 34) {
117 flog_err(EC_ZEBRA_IRDP_LEN_MISMATCH
,
118 "IRDP: RX ICMP packet too long from %s\n",
123 icmp
= (struct icmphdr
*)(p
+ ip_hlen
);
125 saved_chksum
= icmp
->checksum
;
127 /* check icmp checksum */
128 if (in_cksum(icmp
, datalen
) != saved_chksum
) {
130 EC_ZEBRA_IRDP_BAD_CHECKSUM
,
131 "IRDP: RX ICMP packet from %s. Bad checksum, silently ignored",
136 /* Handle just only IRDP */
137 if (!(icmp
->type
== ICMP_ROUTERADVERT
138 || icmp
->type
== ICMP_ROUTERSOLICIT
))
141 if (icmp
->code
!= 0) {
143 EC_ZEBRA_IRDP_BAD_TYPE_CODE
,
144 "IRDP: RX packet type %d from %s. Bad ICMP type code, silently ignored",
145 icmp
->type
, inet_ntoa(src
));
149 if (!((ntohl(ip
->ip_dst
.s_addr
) == INADDR_BROADCAST
)
150 && (irdp
->flags
& IF_BROADCAST
))
151 || (ntohl(ip
->ip_dst
.s_addr
) == INADDR_ALLRTRS_GROUP
152 && !(irdp
->flags
& IF_BROADCAST
))) {
154 EC_ZEBRA_IRDP_BAD_RX_FLAGS
,
155 "IRDP: RX illegal from %s to %s while %s operates in %s; Please correct settings\n",
157 ntohl(ip
->ip_dst
.s_addr
) == INADDR_ALLRTRS_GROUP
159 : inet_ntoa(ip
->ip_dst
),
161 irdp
->flags
& IF_BROADCAST
? "broadcast" : "multicast");
165 switch (icmp
->type
) {
166 case ICMP_ROUTERADVERT
:
169 case ICMP_ROUTERSOLICIT
:
171 if (irdp
->flags
& IF_DEBUG_MESSAGES
)
172 zlog_debug("IRDP: RX Solicit on %s from %s",
173 ifp
->name
, inet_ntoa(src
));
175 process_solicit(ifp
);
180 EC_ZEBRA_IRDP_BAD_TYPE_CODE
,
181 "IRDP: RX packet type %d from %s. Bad ICMP type code, silently ignored",
182 icmp
->type
, inet_ntoa(src
));
186 static int irdp_recvmsg(int sock
, uint8_t *buf
, int size
, int *ifindex
)
190 char adata
[CMSG_SPACE(SOPT_SIZE_CMSG_PKTINFO_IPV4())];
193 memset(&msg
, 0, sizeof(msg
));
194 msg
.msg_name
= (void *)0;
198 msg
.msg_control
= (void *)adata
;
199 msg
.msg_controllen
= sizeof(adata
);
204 ret
= recvmsg(sock
, &msg
, 0);
206 flog_warn(EC_LIB_SOCKET
, "IRDP: recvmsg: read error %s",
207 safe_strerror(errno
));
211 if (msg
.msg_flags
& MSG_TRUNC
) {
212 flog_warn(EC_LIB_SOCKET
, "IRDP: recvmsg: truncated message");
215 if (msg
.msg_flags
& MSG_CTRUNC
) {
216 flog_warn(EC_LIB_SOCKET
,
217 "IRDP: recvmsg: truncated control message");
221 *ifindex
= getsockopt_ifindex(AF_INET
, &msg
);
226 int irdp_read_raw(struct thread
*r
)
228 struct interface
*ifp
;
230 struct irdp_interface
*irdp
;
231 char buf
[IRDP_RX_BUF
];
232 int ret
, ifindex
= 0;
234 int irdp_sock
= THREAD_FD(r
);
236 thread_add_read(zrouter
.master
, irdp_read_raw
, NULL
, irdp_sock
,
239 ret
= irdp_recvmsg(irdp_sock
, (uint8_t *)buf
, IRDP_RX_BUF
, &ifindex
);
242 flog_warn(EC_LIB_SOCKET
, "IRDP: RX Error length = %d", ret
);
244 ifp
= if_lookup_by_index(ifindex
, VRF_DEFAULT
);
256 if (!(irdp
->flags
& IF_ACTIVE
)) {
258 if (irdp
->flags
& IF_DEBUG_MISC
)
259 zlog_debug("IRDP: RX ICMP for disabled interface %s",
264 if (irdp
->flags
& IF_DEBUG_PACKET
) {
266 zlog_debug("IRDP: RX (idx %d) ", ifindex
);
267 for (i
= 0; i
< ret
; i
++)
268 zlog_debug("IRDP: RX %x ", buf
[i
] & 0xFF);
271 parse_irdp_packet(buf
, ret
, ifp
);
276 void send_packet(struct interface
*ifp
, struct stream
*s
, uint32_t dst
,
277 struct prefix
*p
, uint32_t ttl
)
279 static struct sockaddr_in sockdst
= {AF_INET
};
281 struct icmphdr
*icmp
;
283 struct cmsghdr
*cmsg
;
284 struct iovec iovector
;
287 struct in_pktinfo
*pktinfo
;
291 if (!(ifp
->flags
& IFF_UP
))
295 src
= ntohl(p
->u
.prefix4
.s_addr
);
297 src
= 0; /* Is filled in */
299 ip
= (struct ip
*)buf
;
300 ip
->ip_hl
= sizeof(struct ip
) >> 2;
301 ip
->ip_v
= IPVERSION
;
304 ip
->ip_p
= 1; /* IP_ICMP */
306 ip
->ip_src
.s_addr
= src
;
307 ip
->ip_dst
.s_addr
= dst
;
308 icmp
= (struct icmphdr
*)(buf
+ sizeof(struct ip
));
310 /* Merge IP header with icmp packet */
311 assert(stream_get_endp(s
) < (sizeof(buf
) - sizeof(struct ip
)));
312 stream_get(icmp
, s
, stream_get_endp(s
));
314 /* icmp->checksum is already calculated */
315 ip
->ip_len
= sizeof(struct ip
) + stream_get_endp(s
);
318 if (setsockopt(irdp_sock
, IPPROTO_IP
, IP_HDRINCL
, (char *)&on
,
321 flog_err(EC_LIB_SOCKET
,
322 "IRDP: Cannot set IP_HDRINCLU %s(%d) on %s",
323 safe_strerror(errno
), errno
, ifp
->name
);
326 if (dst
== INADDR_BROADCAST
) {
329 if (setsockopt(irdp_sock
, SOL_SOCKET
, SO_BROADCAST
, &bon
,
332 flog_err(EC_LIB_SOCKET
,
333 "IRDP: Cannot set SO_BROADCAST %s(%d) on %s",
334 safe_strerror(errno
), errno
, ifp
->name
);
337 if (dst
!= INADDR_BROADCAST
)
338 setsockopt_ipv4_multicast_loop(irdp_sock
, 0);
340 memset(&sockdst
, 0, sizeof(sockdst
));
341 sockdst
.sin_family
= AF_INET
;
342 sockdst
.sin_addr
.s_addr
= dst
;
344 cmsg
= (struct cmsghdr
*)(msgbuf
+ sizeof(struct msghdr
));
345 cmsg
->cmsg_len
= sizeof(struct cmsghdr
) + sizeof(struct in_pktinfo
);
346 cmsg
->cmsg_level
= SOL_IP
;
347 cmsg
->cmsg_type
= IP_PKTINFO
;
348 pktinfo
= (struct in_pktinfo
*)CMSG_DATA(cmsg
);
349 pktinfo
->ipi_ifindex
= ifp
->ifindex
;
350 pktinfo
->ipi_spec_dst
.s_addr
= src
;
351 pktinfo
->ipi_addr
.s_addr
= src
;
353 iovector
.iov_base
= (void *)buf
;
354 iovector
.iov_len
= ip
->ip_len
;
355 msg
= (struct msghdr
*)msgbuf
;
356 msg
->msg_name
= &sockdst
;
357 msg
->msg_namelen
= sizeof(sockdst
);
358 msg
->msg_iov
= &iovector
;
360 msg
->msg_control
= cmsg
;
361 msg
->msg_controllen
= cmsg
->cmsg_len
;
363 sockopt_iphdrincl_swab_htosys(ip
);
365 if (sendmsg(irdp_sock
, msg
, 0) < 0)
366 flog_err(EC_LIB_SOCKET
,
367 "IRDP: sendmsg send failure %s(%d) on %s",
368 safe_strerror(errno
), errno
, ifp
->name
);