diff options
Diffstat (limited to 'zebra/irdp_packet.c')
-rw-r--r-- | zebra/irdp_packet.c | 352 |
1 files changed, 352 insertions, 0 deletions
diff --git a/zebra/irdp_packet.c b/zebra/irdp_packet.c new file mode 100644 index 0000000..2dfc027 --- /dev/null +++ b/zebra/irdp_packet.c @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright (C) 2000 Robert Olsson. + * Swedish University of Agricultural Sciences + */ + +/* + * This work includes work with the following copywrite: + * + * Copyright (C) 1997, 2000 Kunihiro Ishiguro + * + */ + +/* + * Thanks to Jens Laas at Swedish University of Agricultural Sciences + * for reviewing and tests. + */ + + +#include <zebra.h> +#include <netinet/ip_icmp.h> + +#include "checksum.h" +#include "command.h" +#include "connected.h" +#include "if.h" +#include "ioctl.h" +#include "log.h" +#include "log.h" +#include "memory.h" +#include "prefix.h" +#include "sockopt.h" +#include "sockunion.h" +#include "sockunion.h" +#include "stream.h" +#include "frrevent.h" +#include "vty.h" +#include "zclient.h" +#include "lib_errors.h" + +#include "zebra/interface.h" +#include "zebra/rtadv.h" +#include "zebra/rib.h" +#include "zebra/zebra_router.h" +#include "zebra/redistribute.h" +#include "zebra/irdp.h" +#include "zebra/zebra_errors.h" + + +/* GLOBAL VARS */ + +int irdp_sock = -1; + +extern struct event *t_irdp_raw; + +static void parse_irdp_packet(char *p, int len, struct interface *ifp) +{ + struct ip *ip = (struct ip *)p; + struct icmphdr *icmp; + struct in_addr src; + int ip_hlen, iplen, datalen; + struct zebra_if *zi; + struct irdp_interface *irdp; + uint16_t saved_chksum; + char buf[PREFIX_STRLEN]; + + zi = ifp->info; + if (!zi) + return; + + irdp = zi->irdp; + if (!irdp) + return; + + ip_hlen = ip->ip_hl << 2; + + sockopt_iphdrincl_swab_systoh(ip); + + iplen = ip->ip_len; + datalen = len - ip_hlen; + src = ip->ip_src; + + if (len != iplen) { + flog_err(EC_ZEBRA_IRDP_LEN_MISMATCH, + "IRDP: RX length doesn't match IP length"); + return; + } + + if (iplen < ICMP_MINLEN) { + flog_err(EC_ZEBRA_IRDP_LEN_MISMATCH, + "IRDP: RX ICMP packet too short from %pI4", + &src); + return; + } + + /* XXX: RAW doesn't receive link-layer, surely? ??? */ + /* Check so we don't checksum packets longer than oure RX_BUF - (ethlen + + + len of IP-header) 14+20 */ + if (iplen > IRDP_RX_BUF - 34) { + flog_err(EC_ZEBRA_IRDP_LEN_MISMATCH, + "IRDP: RX ICMP packet too long from %pI4", + &src); + return; + } + + icmp = (struct icmphdr *)(p + ip_hlen); + + saved_chksum = icmp->checksum; + icmp->checksum = 0; + /* check icmp checksum */ + if (in_cksum(icmp, datalen) != saved_chksum) { + flog_warn( + EC_ZEBRA_IRDP_BAD_CHECKSUM, + "IRDP: RX ICMP packet from %pI4 Bad checksum, silently ignored", + &src); + return; + } + + /* Handle just only IRDP */ + if (!(icmp->type == ICMP_ROUTERADVERT + || icmp->type == ICMP_ROUTERSOLICIT)) + return; + + if (icmp->code != 0) { + flog_warn( + EC_ZEBRA_IRDP_BAD_TYPE_CODE, + "IRDP: RX packet type %d from %pI4 Bad ICMP type code, silently ignored", + icmp->type, &src); + return; + } + + if (!((ntohl(ip->ip_dst.s_addr) == INADDR_BROADCAST) + && (irdp->flags & IF_BROADCAST)) + || (ntohl(ip->ip_dst.s_addr) == INADDR_ALLRTRS_GROUP + && !(irdp->flags & IF_BROADCAST))) { + flog_warn( + EC_ZEBRA_IRDP_BAD_RX_FLAGS, + "IRDP: RX illegal from %pI4 to %s while %s operates in %s; Please correct settings", + &src, + ntohl(ip->ip_dst.s_addr) == INADDR_ALLRTRS_GROUP + ? "multicast" + : inet_ntop(AF_INET, &ip->ip_dst, + buf, sizeof(buf)), + ifp->name, + irdp->flags & IF_BROADCAST ? "broadcast" : "multicast"); + return; + } + + switch (icmp->type) { + case ICMP_ROUTERADVERT: + break; + + case ICMP_ROUTERSOLICIT: + + if (irdp->flags & IF_DEBUG_MESSAGES) + zlog_debug("IRDP: RX Solicit on %s from %pI4", + ifp->name, &src); + + process_solicit(ifp); + break; + + default: + flog_warn( + EC_ZEBRA_IRDP_BAD_TYPE_CODE, + "IRDP: RX packet type %d from %pI4 Bad ICMP type code, silently ignored", + icmp->type, &src); + } +} + +static int irdp_recvmsg(int sock, uint8_t *buf, int size, int *ifindex) +{ + struct msghdr msg; + struct iovec iov; + char adata[CMSG_SPACE(SOPT_SIZE_CMSG_PKTINFO_IPV4())]; + int ret; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = (void *)0; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = (void *)adata; + msg.msg_controllen = sizeof(adata); + + iov.iov_base = buf; + iov.iov_len = size; + + ret = recvmsg(sock, &msg, 0); + if (ret < 0) { + flog_warn(EC_LIB_SOCKET, "IRDP: recvmsg: read error %s", + safe_strerror(errno)); + return ret; + } + + if (msg.msg_flags & MSG_TRUNC) { + flog_warn(EC_LIB_SOCKET, "IRDP: recvmsg: truncated message"); + return ret; + } + if (msg.msg_flags & MSG_CTRUNC) { + flog_warn(EC_LIB_SOCKET, + "IRDP: recvmsg: truncated control message"); + return ret; + } + + *ifindex = getsockopt_ifindex(AF_INET, &msg); + + return ret; +} + +void irdp_read_raw(struct event *r) +{ + struct interface *ifp; + struct zebra_if *zi; + struct irdp_interface *irdp; + char buf[IRDP_RX_BUF]; + int ret, ifindex = 0; + + int irdp_sock = EVENT_FD(r); + event_add_read(zrouter.master, irdp_read_raw, NULL, irdp_sock, + &t_irdp_raw); + + ret = irdp_recvmsg(irdp_sock, (uint8_t *)buf, IRDP_RX_BUF, &ifindex); + + if (ret < 0) + flog_warn(EC_LIB_SOCKET, "IRDP: RX Error length = %d", ret); + + ifp = if_lookup_by_index(ifindex, VRF_DEFAULT); + if (!ifp) + return; + + zi = ifp->info; + if (!zi) + return; + + irdp = zi->irdp; + if (!irdp) + return; + + if (!(irdp->flags & IF_ACTIVE)) { + + if (irdp->flags & IF_DEBUG_MISC) + zlog_debug("IRDP: RX ICMP for disabled interface %s", + ifp->name); + return; + } + + if (irdp->flags & IF_DEBUG_PACKET) { + int i; + zlog_debug("IRDP: RX (idx %d) ", ifindex); + for (i = 0; i < ret; i++) + zlog_debug("IRDP: RX %x ", buf[i] & 0xFF); + } + + parse_irdp_packet(buf, ret, ifp); +} + +void send_packet(struct interface *ifp, struct stream *s, uint32_t dst, + struct prefix *p, uint32_t ttl) +{ + static struct sockaddr_in sockdst = {AF_INET}; + struct ip *ip; + struct icmphdr *icmp; + struct msghdr *msg; + struct cmsghdr *cmsg; + struct iovec iovector; + char msgbuf[256]; + char buf[256]; + struct in_pktinfo *pktinfo; + unsigned long src; + uint8_t on; + + if (!(ifp->flags & IFF_UP)) + return; + + if (p) + src = ntohl(p->u.prefix4.s_addr); + else + src = 0; /* Is filled in */ + + ip = (struct ip *)buf; + ip->ip_hl = sizeof(struct ip) >> 2; + ip->ip_v = IPVERSION; + ip->ip_tos = 0xC0; + ip->ip_off = 0L; + ip->ip_p = 1; /* IP_ICMP */ + ip->ip_ttl = ttl; + ip->ip_src.s_addr = src; + ip->ip_dst.s_addr = dst; + icmp = (struct icmphdr *)(buf + sizeof(struct ip)); + + /* Merge IP header with icmp packet */ + assert(stream_get_endp(s) < (sizeof(buf) - sizeof(struct ip))); + stream_get(icmp, s, stream_get_endp(s)); + + /* icmp->checksum is already calculated */ + ip->ip_len = sizeof(struct ip) + stream_get_endp(s); + + on = 1; + if (setsockopt(irdp_sock, IPPROTO_IP, IP_HDRINCL, (char *)&on, + sizeof(on)) + < 0) + flog_err(EC_LIB_SOCKET, + "IRDP: Cannot set IP_HDRINCLU %s(%d) on %s", + safe_strerror(errno), errno, ifp->name); + + + if (dst == INADDR_BROADCAST) { + uint32_t bon = 1; + + if (setsockopt(irdp_sock, SOL_SOCKET, SO_BROADCAST, &bon, + sizeof(bon)) + < 0) + flog_err(EC_LIB_SOCKET, + "IRDP: Cannot set SO_BROADCAST %s(%d) on %s", + safe_strerror(errno), errno, ifp->name); + } + + if (dst != INADDR_BROADCAST) + setsockopt_ipv4_multicast_loop(irdp_sock, 0); + + memset(&sockdst, 0, sizeof(sockdst)); + sockdst.sin_family = AF_INET; + sockdst.sin_addr.s_addr = dst; + + cmsg = (struct cmsghdr *)(msgbuf + sizeof(struct msghdr)); + cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(struct in_pktinfo); + cmsg->cmsg_level = SOL_IP; + cmsg->cmsg_type = IP_PKTINFO; + pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); + pktinfo->ipi_ifindex = ifp->ifindex; + pktinfo->ipi_spec_dst.s_addr = src; + pktinfo->ipi_addr.s_addr = src; + + iovector.iov_base = (void *)buf; + iovector.iov_len = ip->ip_len; + msg = (struct msghdr *)msgbuf; + msg->msg_name = &sockdst; + msg->msg_namelen = sizeof(sockdst); + msg->msg_iov = &iovector; + msg->msg_iovlen = 1; + msg->msg_control = cmsg; + msg->msg_controllen = cmsg->cmsg_len; + + sockopt_iphdrincl_swab_htosys(ip); + + if (sendmsg(irdp_sock, msg, 0) < 0) + flog_err(EC_LIB_SOCKET, + "IRDP: sendmsg send failure %s(%d) on %s", + safe_strerror(errno), errno, ifp->name); +} |