702 lines
18 KiB
C
702 lines
18 KiB
C
/* $OpenBSD: packet.c,v 1.20 2021/01/19 11:49:26 claudio Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
|
|
* Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <net/if_dl.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/ip.h>
|
|
|
|
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "eigrpd.h"
|
|
#include "eigrpe.h"
|
|
#include "log.h"
|
|
|
|
static int send_packet_v4(struct iface *, struct nbr *, struct ibuf *);
|
|
static int send_packet_v6(struct iface *, struct nbr *, struct ibuf *);
|
|
static int recv_packet_nbr(struct nbr *, struct eigrp_hdr *,
|
|
struct seq_addr_head *, struct tlv_mcast_seq *);
|
|
static void recv_packet_eigrp(int, union eigrpd_addr *,
|
|
union eigrpd_addr *, struct iface *, struct eigrp_hdr *,
|
|
char *, uint16_t);
|
|
static int eigrp_hdr_sanity_check(int, union eigrpd_addr *,
|
|
struct eigrp_hdr *, uint16_t, const struct iface *);
|
|
static struct iface *find_iface(unsigned int, int, union eigrpd_addr *);
|
|
|
|
int
|
|
gen_eigrp_hdr(struct ibuf *buf, uint16_t opcode, uint8_t flags,
|
|
uint32_t seq_num, uint16_t as)
|
|
{
|
|
struct eigrp_hdr eigrp_hdr;
|
|
|
|
memset(&eigrp_hdr, 0, sizeof(eigrp_hdr));
|
|
eigrp_hdr.version = EIGRP_VERSION;
|
|
eigrp_hdr.opcode = opcode;
|
|
/* chksum will be set later */
|
|
eigrp_hdr.flags = htonl(flags);
|
|
eigrp_hdr.seq_num = htonl(seq_num);
|
|
/* ack_num will be set later */
|
|
eigrp_hdr.vrid = htons(EIGRP_VRID_UNICAST_AF);
|
|
eigrp_hdr.as = htons(as);
|
|
|
|
return (ibuf_add(buf, &eigrp_hdr, sizeof(eigrp_hdr)));
|
|
}
|
|
|
|
/* send and receive packets */
|
|
static int
|
|
send_packet_v4(struct iface *iface, struct nbr *nbr, struct ibuf *buf)
|
|
{
|
|
struct sockaddr_in dst;
|
|
struct msghdr msg;
|
|
struct iovec iov[2];
|
|
struct ip ip_hdr;
|
|
|
|
/* setup sockaddr */
|
|
dst.sin_family = AF_INET;
|
|
dst.sin_len = sizeof(struct sockaddr_in);
|
|
if (nbr)
|
|
dst.sin_addr = nbr->addr.v4;
|
|
else
|
|
dst.sin_addr = global.mcast_addr_v4;
|
|
|
|
/* setup IP hdr */
|
|
memset(&ip_hdr, 0, sizeof(ip_hdr));
|
|
ip_hdr.ip_v = IPVERSION;
|
|
ip_hdr.ip_hl = sizeof(ip_hdr) >> 2;
|
|
ip_hdr.ip_tos = IPTOS_PREC_INTERNETCONTROL;
|
|
ip_hdr.ip_len = htons(ibuf_size(buf) + sizeof(ip_hdr));
|
|
ip_hdr.ip_id = 0; /* 0 means kernel set appropriate value */
|
|
ip_hdr.ip_off = 0;
|
|
ip_hdr.ip_ttl = EIGRP_IP_TTL;
|
|
ip_hdr.ip_p = IPPROTO_EIGRP;
|
|
ip_hdr.ip_sum = 0;
|
|
ip_hdr.ip_src.s_addr = if_primary_addr(iface);
|
|
ip_hdr.ip_dst = dst.sin_addr;
|
|
|
|
/* setup buffer */
|
|
memset(&msg, 0, sizeof(msg));
|
|
iov[0].iov_base = &ip_hdr;
|
|
iov[0].iov_len = sizeof(ip_hdr);
|
|
iov[1].iov_base = buf->buf;
|
|
iov[1].iov_len = ibuf_size(buf);
|
|
msg.msg_name = &dst;
|
|
msg.msg_namelen = sizeof(dst);
|
|
msg.msg_iov = iov;
|
|
msg.msg_iovlen = 2;
|
|
|
|
/* set outgoing interface for multicast traffic */
|
|
if (IN_MULTICAST(ntohl(dst.sin_addr.s_addr)))
|
|
if (if_set_ipv4_mcast(iface) == -1) {
|
|
log_warn("%s: error setting multicast interface, %s",
|
|
__func__, iface->name);
|
|
return (-1);
|
|
}
|
|
|
|
if (sendmsg(global.eigrp_socket_v4, &msg, 0) == -1) {
|
|
log_warn("%s: error sending packet on interface %s",
|
|
__func__, iface->name);
|
|
return (-1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
send_packet_v6(struct iface *iface, struct nbr *nbr, struct ibuf *buf)
|
|
{
|
|
struct sockaddr_in6 sa6;
|
|
|
|
/* setup sockaddr */
|
|
memset(&sa6, 0, sizeof(sa6));
|
|
sa6.sin6_family = AF_INET6;
|
|
sa6.sin6_len = sizeof(struct sockaddr_in6);
|
|
if (nbr) {
|
|
sa6.sin6_addr = nbr->addr.v6;
|
|
addscope(&sa6, iface->ifindex);
|
|
} else
|
|
sa6.sin6_addr = global.mcast_addr_v6;
|
|
|
|
/* set outgoing interface for multicast traffic */
|
|
if (IN6_IS_ADDR_MULTICAST(&sa6.sin6_addr))
|
|
if (if_set_ipv6_mcast(iface) == -1) {
|
|
log_warn("%s: error setting multicast interface, %s",
|
|
__func__, iface->name);
|
|
return (-1);
|
|
}
|
|
|
|
if (sendto(global.eigrp_socket_v6, buf->buf, buf->wpos, 0,
|
|
(struct sockaddr *)&sa6, sizeof(sa6)) == -1) {
|
|
log_warn("%s: error sending packet on interface %s",
|
|
__func__, iface->name);
|
|
return (-1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
send_packet(struct eigrp_iface *ei, struct nbr *nbr, uint32_t flags,
|
|
struct ibuf *buf)
|
|
{
|
|
struct eigrp *eigrp = ei->eigrp;
|
|
struct iface *iface = ei->iface;
|
|
struct eigrp_hdr *eigrp_hdr;
|
|
|
|
if (!(iface->flags & IFF_UP) || !LINK_STATE_IS_UP(iface->linkstate))
|
|
return (-1);
|
|
|
|
/* update ack number, flags and checksum */
|
|
if ((eigrp_hdr = ibuf_seek(buf, 0, sizeof(*eigrp_hdr))) == NULL)
|
|
fatalx("send_packet: buf_seek failed");
|
|
if (nbr) {
|
|
eigrp_hdr->ack_num = htonl(nbr->recv_seq);
|
|
rtp_ack_stop_timer(nbr);
|
|
}
|
|
if (flags) {
|
|
eigrp_hdr->flags = ntohl(eigrp_hdr->flags) | flags;
|
|
eigrp_hdr->flags = htonl(eigrp_hdr->flags);
|
|
}
|
|
eigrp_hdr->chksum = 0;
|
|
eigrp_hdr->chksum = in_cksum(buf->buf, ibuf_size(buf));
|
|
|
|
/* log packet being sent */
|
|
if (eigrp_hdr->opcode != EIGRP_OPC_HELLO) {
|
|
char buffer[64];
|
|
|
|
if (nbr)
|
|
snprintf(buffer, sizeof(buffer), "nbr %s",
|
|
log_addr(eigrp->af, &nbr->addr));
|
|
else
|
|
snprintf(buffer, sizeof(buffer), "(multicast)");
|
|
|
|
log_debug("%s: type %s iface %s %s AS %u seq %u ack %u",
|
|
__func__, opcode_name(eigrp_hdr->opcode), iface->name,
|
|
buffer, ntohs(eigrp_hdr->as), ntohl(eigrp_hdr->seq_num),
|
|
ntohl(eigrp_hdr->ack_num));
|
|
}
|
|
|
|
switch (eigrp->af) {
|
|
case AF_INET:
|
|
if (send_packet_v4(iface, nbr, buf) < 0)
|
|
return (-1);
|
|
break;
|
|
case AF_INET6:
|
|
if (send_packet_v6(iface, nbr, buf) < 0)
|
|
return (-1);
|
|
break;
|
|
default:
|
|
fatalx("send_packet: unknown af");
|
|
}
|
|
|
|
switch (eigrp_hdr->opcode) {
|
|
case EIGRP_OPC_HELLO:
|
|
if (ntohl(eigrp_hdr->ack_num) == 0)
|
|
ei->eigrp->stats.hellos_sent++;
|
|
else
|
|
ei->eigrp->stats.acks_sent++;
|
|
break;
|
|
case EIGRP_OPC_UPDATE:
|
|
ei->eigrp->stats.updates_sent++;
|
|
break;
|
|
case EIGRP_OPC_QUERY:
|
|
ei->eigrp->stats.queries_sent++;
|
|
break;
|
|
case EIGRP_OPC_REPLY:
|
|
ei->eigrp->stats.replies_sent++;
|
|
break;
|
|
case EIGRP_OPC_SIAQUERY:
|
|
ei->eigrp->stats.squeries_sent++;
|
|
break;
|
|
case EIGRP_OPC_SIAREPLY:
|
|
ei->eigrp->stats.sreplies_sent++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
recv_packet_nbr(struct nbr *nbr, struct eigrp_hdr *eigrp_hdr,
|
|
struct seq_addr_head *seq_addr_list, struct tlv_mcast_seq *tm)
|
|
{
|
|
uint32_t seq, ack;
|
|
struct seq_addr_entry *sa;
|
|
|
|
seq = ntohl(eigrp_hdr->seq_num);
|
|
ack = ntohl(eigrp_hdr->ack_num);
|
|
|
|
/*
|
|
* draft-savage-eigrp-04 - Section 5.3.1:
|
|
* "In addition to the HELLO packet, if any packet is received within
|
|
* the hold time period, then the Hold Time period will be reset."
|
|
*/
|
|
nbr_start_timeout(nbr);
|
|
|
|
/* handle the sequence tlv */
|
|
if (eigrp_hdr->opcode == EIGRP_OPC_HELLO &&
|
|
!TAILQ_EMPTY(seq_addr_list)) {
|
|
nbr->flags |= F_EIGRP_NBR_CR_MODE;
|
|
|
|
TAILQ_FOREACH(sa, seq_addr_list, entry) {
|
|
switch (sa->af) {
|
|
case AF_INET:
|
|
if (sa->addr.v4.s_addr ==
|
|
if_primary_addr(nbr->ei->iface)) {
|
|
nbr->flags &= ~F_EIGRP_NBR_CR_MODE;
|
|
break;
|
|
}
|
|
break;
|
|
case AF_INET6:
|
|
if (IN6_ARE_ADDR_EQUAL(&sa->addr.v6,
|
|
&nbr->ei->iface->linklocal)) {
|
|
nbr->flags &= ~F_EIGRP_NBR_CR_MODE;
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (tm)
|
|
nbr->next_mcast_seq = ntohl(tm->seq);
|
|
}
|
|
|
|
if ((ntohl(eigrp_hdr->flags) & EIGRP_HDR_FLAG_CR)) {
|
|
if (!(nbr->flags & F_EIGRP_NBR_CR_MODE))
|
|
return (-1);
|
|
nbr->flags &= ~F_EIGRP_NBR_CR_MODE;
|
|
if (ntohl(eigrp_hdr->seq_num) != nbr->next_mcast_seq)
|
|
return (-1);
|
|
}
|
|
|
|
/* ack processing */
|
|
if (ack != 0)
|
|
rtp_process_ack(nbr, ack);
|
|
if (seq != 0) {
|
|
/* check for sequence wraparound */
|
|
if (nbr->recv_seq >= seq &&
|
|
!(nbr->recv_seq == UINT32_MAX && seq == 1)) {
|
|
log_debug("%s: duplicate packet", __func__);
|
|
rtp_send_ack(nbr);
|
|
return (-1);
|
|
}
|
|
nbr->recv_seq = seq;
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
recv_packet_eigrp(int af, union eigrpd_addr *src, union eigrpd_addr *dest,
|
|
struct iface *iface, struct eigrp_hdr *eigrp_hdr, char *buf, uint16_t len)
|
|
{
|
|
struct eigrp_iface *ei;
|
|
struct nbr *nbr;
|
|
struct tlv_parameter *tp = NULL;
|
|
struct tlv_sw_version *tv = NULL;
|
|
struct tlv_mcast_seq *tm = NULL;
|
|
struct rinfo ri;
|
|
struct rinfo_entry *re;
|
|
struct seq_addr_head seq_addr_list;
|
|
struct rinfo_head rinfo_list;
|
|
|
|
/* EIGRP header sanity checks */
|
|
if (eigrp_hdr_sanity_check(af, dest, eigrp_hdr, len, iface) == -1)
|
|
return;
|
|
|
|
buf += sizeof(*eigrp_hdr);
|
|
len -= sizeof(*eigrp_hdr);
|
|
|
|
TAILQ_INIT(&seq_addr_list);
|
|
TAILQ_INIT(&rinfo_list);
|
|
while (len > 0) {
|
|
struct tlv tlv;
|
|
uint16_t tlv_type;
|
|
|
|
if (len < sizeof(tlv)) {
|
|
log_debug("%s: malformed packet (bad length)",
|
|
__func__);
|
|
goto error;
|
|
}
|
|
|
|
memcpy(&tlv, buf, sizeof(tlv));
|
|
if (ntohs(tlv.length) > len) {
|
|
log_debug("%s: malformed packet (bad length)",
|
|
__func__);
|
|
goto error;
|
|
}
|
|
|
|
tlv_type = ntohs(tlv.type);
|
|
switch (tlv_type) {
|
|
case TLV_TYPE_PARAMETER:
|
|
if ((tp = tlv_decode_parameter(&tlv, buf)) == NULL)
|
|
goto error;
|
|
break;
|
|
case TLV_TYPE_SEQ:
|
|
if (tlv_decode_seq(af, &tlv, buf, &seq_addr_list) < 0)
|
|
goto error;
|
|
break;
|
|
case TLV_TYPE_SW_VERSION:
|
|
if ((tv = tlv_decode_sw_version(&tlv, buf)) == NULL)
|
|
goto error;
|
|
break;
|
|
case TLV_TYPE_MCAST_SEQ:
|
|
if ((tm = tlv_decode_mcast_seq(&tlv, buf)) == NULL)
|
|
goto error;
|
|
break;
|
|
case TLV_TYPE_IPV4_INTERNAL:
|
|
case TLV_TYPE_IPV4_EXTERNAL:
|
|
case TLV_TYPE_IPV6_INTERNAL:
|
|
case TLV_TYPE_IPV6_EXTERNAL:
|
|
/* silently ignore TLV from different address-family */
|
|
if ((tlv_type & TLV_PROTO_MASK) == TLV_PROTO_IPV4 &&
|
|
af != AF_INET)
|
|
break;
|
|
if ((tlv_type & TLV_PROTO_MASK) == TLV_PROTO_IPV6 &&
|
|
af != AF_INET6)
|
|
break;
|
|
|
|
if (tlv_decode_route(af, &tlv, buf, &ri) < 0)
|
|
goto error;
|
|
if ((re = calloc(1, sizeof(*re))) == NULL)
|
|
fatal("recv_packet_eigrp");
|
|
re->rinfo = ri;
|
|
TAILQ_INSERT_TAIL(&rinfo_list, re, entry);
|
|
break;
|
|
case TLV_TYPE_AUTH:
|
|
case TLV_TYPE_PEER_TERM:
|
|
/*
|
|
* XXX There is no enough information in the draft
|
|
* to implement these TLVs properly.
|
|
*/
|
|
case TLV_TYPE_IPV4_COMMUNITY:
|
|
case TLV_TYPE_IPV6_COMMUNITY:
|
|
/* TODO */
|
|
default:
|
|
/* ignore unknown tlv */
|
|
break;
|
|
}
|
|
buf += ntohs(tlv.length);
|
|
len -= ntohs(tlv.length);
|
|
}
|
|
|
|
ei = eigrp_if_lookup(iface, af, ntohs(eigrp_hdr->as));
|
|
if (ei == NULL || ei->passive)
|
|
goto error;
|
|
|
|
nbr = nbr_find(ei, src);
|
|
if (nbr == NULL && (eigrp_hdr->opcode != EIGRP_OPC_HELLO ||
|
|
ntohl(eigrp_hdr->ack_num) != 0)) {
|
|
log_debug("%s: unknown neighbor", __func__);
|
|
goto error;
|
|
} else if (nbr && recv_packet_nbr(nbr, eigrp_hdr, &seq_addr_list,
|
|
tm) < 0)
|
|
goto error;
|
|
|
|
/* log packet being received */
|
|
if (eigrp_hdr->opcode != EIGRP_OPC_HELLO)
|
|
log_debug("%s: type %s nbr %s AS %u seq %u ack %u", __func__,
|
|
opcode_name(eigrp_hdr->opcode), log_addr(af, &nbr->addr),
|
|
ei->eigrp->as, ntohl(eigrp_hdr->seq_num),
|
|
ntohl(eigrp_hdr->ack_num));
|
|
|
|
/* switch EIGRP packet type */
|
|
switch (eigrp_hdr->opcode) {
|
|
case EIGRP_OPC_HELLO:
|
|
if (ntohl(eigrp_hdr->ack_num) == 0) {
|
|
recv_hello(ei, src, nbr, tp);
|
|
ei->eigrp->stats.hellos_recv++;
|
|
} else
|
|
ei->eigrp->stats.acks_recv++;
|
|
break;
|
|
case EIGRP_OPC_UPDATE:
|
|
recv_update(nbr, &rinfo_list, ntohl(eigrp_hdr->flags));
|
|
ei->eigrp->stats.updates_recv++;
|
|
break;
|
|
case EIGRP_OPC_QUERY:
|
|
recv_query(nbr, &rinfo_list, 0);
|
|
ei->eigrp->stats.queries_recv++;
|
|
break;
|
|
case EIGRP_OPC_REPLY:
|
|
recv_reply(nbr, &rinfo_list, 0);
|
|
ei->eigrp->stats.replies_recv++;
|
|
break;
|
|
case EIGRP_OPC_SIAQUERY:
|
|
recv_query(nbr, &rinfo_list, 1);
|
|
ei->eigrp->stats.squeries_recv++;
|
|
break;
|
|
case EIGRP_OPC_SIAREPLY:
|
|
recv_reply(nbr, &rinfo_list, 1);
|
|
ei->eigrp->stats.sreplies_recv++;
|
|
break;
|
|
default:
|
|
log_debug("%s: unknown EIGRP packet type, interface %s",
|
|
__func__, iface->name);
|
|
}
|
|
|
|
error:
|
|
/* free rinfo tlvs */
|
|
message_list_clr(&rinfo_list);
|
|
/* free seq addresses tlvs */
|
|
seq_addr_list_clr(&seq_addr_list);
|
|
}
|
|
|
|
#define CMSG_MAXLEN max(sizeof(struct sockaddr_dl), sizeof(struct in6_pktinfo))
|
|
void
|
|
recv_packet(int fd, short event, void *bula)
|
|
{
|
|
union {
|
|
struct cmsghdr hdr;
|
|
char buf[CMSG_SPACE(CMSG_MAXLEN)];
|
|
} cmsgbuf;
|
|
struct msghdr msg;
|
|
struct sockaddr_storage from;
|
|
struct iovec iov;
|
|
struct ip ip_hdr;
|
|
char pkt[READ_BUF_SIZE];
|
|
char *buf;
|
|
struct cmsghdr *cmsg;
|
|
ssize_t r;
|
|
uint16_t len;
|
|
int af;
|
|
union eigrpd_addr src, dest;
|
|
unsigned int ifindex = 0;
|
|
struct iface *iface;
|
|
struct eigrp_hdr *eigrp_hdr;
|
|
|
|
if (event != EV_READ)
|
|
return;
|
|
|
|
/* setup buffer */
|
|
memset(&msg, 0, sizeof(msg));
|
|
iov.iov_base = buf = pkt;
|
|
iov.iov_len = READ_BUF_SIZE;
|
|
msg.msg_name = &from;
|
|
msg.msg_namelen = sizeof(from);
|
|
msg.msg_iov = &iov;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_control = &cmsgbuf.buf;
|
|
msg.msg_controllen = sizeof(cmsgbuf.buf);
|
|
|
|
if ((r = recvmsg(fd, &msg, 0)) == -1) {
|
|
if (errno != EAGAIN && errno != EINTR)
|
|
log_debug("%s: read error: %s", __func__,
|
|
strerror(errno));
|
|
return;
|
|
}
|
|
len = (uint16_t)r;
|
|
|
|
sa2addr((struct sockaddr *)&from, &af, &src);
|
|
if (bad_addr(af, &src)) {
|
|
log_debug("%s: invalid source address: %s", __func__,
|
|
log_addr(af, &src));
|
|
return;
|
|
}
|
|
|
|
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
|
|
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
|
if (af == AF_INET && cmsg->cmsg_level == IPPROTO_IP &&
|
|
cmsg->cmsg_type == IP_RECVIF) {
|
|
ifindex = ((struct sockaddr_dl *)
|
|
CMSG_DATA(cmsg))->sdl_index;
|
|
break;
|
|
}
|
|
if (af == AF_INET6 && cmsg->cmsg_level == IPPROTO_IPV6 &&
|
|
cmsg->cmsg_type == IPV6_PKTINFO) {
|
|
ifindex = ((struct in6_pktinfo *)
|
|
CMSG_DATA(cmsg))->ipi6_ifindex;
|
|
dest.v6 = ((struct in6_pktinfo *)
|
|
CMSG_DATA(cmsg))->ipi6_addr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* find a matching interface */
|
|
if ((iface = find_iface(ifindex, af, &src)) == NULL)
|
|
return;
|
|
|
|
/* the IPv4 raw sockets API gives us direct access to the IP header */
|
|
if (af == AF_INET) {
|
|
if (len < sizeof(ip_hdr)) {
|
|
log_debug("%s: bad packet size", __func__);
|
|
return;
|
|
}
|
|
memcpy(&ip_hdr, buf, sizeof(ip_hdr));
|
|
if (ntohs(ip_hdr.ip_len) != len) {
|
|
log_debug("%s: invalid IP packet length %u", __func__,
|
|
ntohs(ip_hdr.ip_len));
|
|
return;
|
|
}
|
|
buf += ip_hdr.ip_hl << 2;
|
|
len -= ip_hdr.ip_hl << 2;
|
|
dest.v4 = ip_hdr.ip_dst;
|
|
}
|
|
|
|
/* validate destination address */
|
|
switch (af) {
|
|
case AF_INET:
|
|
/*
|
|
* Packet needs to be sent to 224.0.0.10 or to one of the
|
|
* interface addresses.
|
|
*/
|
|
if (dest.v4.s_addr != global.mcast_addr_v4.s_addr) {
|
|
struct if_addr *if_addr;
|
|
int found = 0;
|
|
|
|
TAILQ_FOREACH(if_addr, &iface->addr_list, entry)
|
|
if (if_addr->af == AF_INET &&
|
|
dest.v4.s_addr == if_addr->addr.v4.s_addr) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
if (found == 0) {
|
|
log_debug("%s: packet sent to wrong address "
|
|
"%s, interface %s", __func__,
|
|
inet_ntoa(dest.v4), iface->name);
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
case AF_INET6:
|
|
/*
|
|
* Packet needs to be sent to ff02::a or to the link local
|
|
* address of the interface.
|
|
*/
|
|
if (!IN6_ARE_ADDR_EQUAL(&dest.v6, &global.mcast_addr_v6) &&
|
|
!IN6_ARE_ADDR_EQUAL(&dest.v6, &iface->linklocal)) {
|
|
log_debug("%s: packet sent to wrong address %s, "
|
|
"interface %s", __func__, log_in6addr(&dest.v6),
|
|
iface->name);
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
fatalx("recv_packet: unknown af");
|
|
break;
|
|
}
|
|
|
|
if (len < sizeof(*eigrp_hdr)) {
|
|
log_debug("%s: bad packet size", __func__);
|
|
return;
|
|
}
|
|
eigrp_hdr = (struct eigrp_hdr *)buf;
|
|
|
|
recv_packet_eigrp(af, &src, &dest, iface, eigrp_hdr, buf, len);
|
|
}
|
|
|
|
static int
|
|
eigrp_hdr_sanity_check(int af, union eigrpd_addr *addr,
|
|
struct eigrp_hdr *eigrp_hdr, uint16_t len, const struct iface *iface)
|
|
{
|
|
if (in_cksum(eigrp_hdr, len)) {
|
|
log_debug("%s: invalid checksum, interface %s", __func__,
|
|
iface->name);
|
|
return (-1);
|
|
}
|
|
|
|
if (eigrp_hdr->version != EIGRP_HEADER_VERSION) {
|
|
log_debug("%s: invalid EIGRP version %d, interface %s",
|
|
__func__, eigrp_hdr->version, iface->name);
|
|
return (-1);
|
|
}
|
|
|
|
if (ntohs(eigrp_hdr->vrid) != EIGRP_VRID_UNICAST_AF) {
|
|
log_debug("%s: unknown or unsupported vrid %u, interface %s",
|
|
__func__, ntohs(eigrp_hdr->vrid), iface->name);
|
|
return (-1);
|
|
}
|
|
|
|
if (eigrp_hdr->opcode == EIGRP_OPC_HELLO &&
|
|
eigrp_hdr->ack_num != 0) {
|
|
switch (af) {
|
|
case AF_INET:
|
|
if (IN_MULTICAST(addr->v4.s_addr)) {
|
|
log_debug("%s: multicast ack (ipv4), "
|
|
"interface %s", __func__, iface->name);
|
|
return (-1);
|
|
}
|
|
break;
|
|
case AF_INET6:
|
|
if (IN6_IS_ADDR_MULTICAST(&addr->v6)) {
|
|
log_debug("%s: multicast ack (ipv6), "
|
|
"interface %s", __func__, iface->name);
|
|
return (-1);
|
|
}
|
|
break;
|
|
default:
|
|
fatalx("eigrp_hdr_sanity_check: unknown af");
|
|
}
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static struct iface *
|
|
find_iface(unsigned int ifindex, int af, union eigrpd_addr *src)
|
|
{
|
|
struct iface *iface;
|
|
struct if_addr *if_addr;
|
|
in_addr_t mask;
|
|
|
|
iface = if_lookup(econf, ifindex);
|
|
if (iface == NULL)
|
|
return (NULL);
|
|
|
|
switch (af) {
|
|
case AF_INET:
|
|
/*
|
|
* From CCNP ROUTE 642-902 OCG:
|
|
* "EIGRP's rules about neighbor IP addresses being in the same
|
|
* subnet are less exact than OSPF. OSPF requires matching
|
|
* subnet numbers and masks. EIGRP just asks the question of
|
|
* whether the neighbor's IP address is in the range of
|
|
* addresses for the subnet as known to the local router."
|
|
*/
|
|
TAILQ_FOREACH(if_addr, &iface->addr_list, entry) {
|
|
if (if_addr->af == AF_INET) {
|
|
mask = prefixlen2mask(if_addr->prefixlen);
|
|
|
|
if ((if_addr->addr.v4.s_addr & mask) ==
|
|
(src->v4.s_addr & mask))
|
|
return (iface);
|
|
}
|
|
}
|
|
break;
|
|
case AF_INET6:
|
|
/*
|
|
* draft-savage-eigrp-04 - Section 10.1:
|
|
* "EIGRP IPv6 will check that a received HELLO contains a valid
|
|
* IPv6 link-local source address."
|
|
*/
|
|
if (IN6_IS_ADDR_LINKLOCAL(&src->v6))
|
|
return (iface);
|
|
break;
|
|
default:
|
|
fatalx("find_iface: unknown af");
|
|
}
|
|
|
|
return (NULL);
|
|
}
|