diff --git a/eigrpd.h b/eigrpd.h
index 9a2500e..8dc6f7a 100644
--- a/eigrpd.h
+++ b/eigrpd.h
@@ -436,6 +436,7 @@ struct ctl_stats {
 };
 
 #define min(x,y) ((x) <= (y) ? (x) : (y))
+#define max(x,y) ((x) > (y) ? (x) : (y))
 
 /* parse.y */
 struct eigrpd_conf	*parse_config(char *);
@@ -480,6 +481,7 @@ void		 embedscope(struct sockaddr_in6 *);
 void		 recoverscope(struct sockaddr_in6 *);
 void		 addscope(struct sockaddr_in6 *, uint32_t);
 void		 clearscope(struct in6_addr *);
+void		 sa2addr(struct sockaddr *, int *, union eigrpd_addr *);
 
 /* eigrpd.c */
 int		 main_imsg_compose_eigrpe(int, pid_t, void *, uint16_t);
diff --git a/eigrpe.c b/eigrpe.c
index 97bc755..61dfcab 100644
--- a/eigrpe.c
+++ b/eigrpe.c
@@ -159,11 +159,11 @@ eigrpe(int debug, int verbose, char *sockname)
 	event_add(&iev_main->ev, NULL);
 
 	event_set(&ev4, global.eigrp_socket_v4, EV_READ|EV_PERSIST,
-	    recv_packet_v4, econf);
+	    recv_packet, econf);
 	event_add(&ev4, NULL);
 
 	event_set(&ev6, global.eigrp_socket_v6, EV_READ|EV_PERSIST,
-	    recv_packet_v6, econf);
+	    recv_packet, econf);
 	event_add(&ev6, NULL);
 
 	/* listen on eigrpd control socket */
diff --git a/eigrpe.h b/eigrpe.h
index 0efba9b..99e24b6 100644
--- a/eigrpe.h
+++ b/eigrpe.h
@@ -157,8 +157,7 @@ int	 gen_eigrp_hdr(struct ibuf *, uint16_t, uint8_t, uint32_t,
 	    uint16_t);
 int	 send_packet(struct eigrp_iface *, struct nbr *, uint32_t,
 	    struct ibuf *);
-void	 recv_packet_v4(int, short, void *);
-void	 recv_packet_v6(int, short, void *);
+void	 recv_packet(int, short, void *);
 
 /* tlv.c */
 int			 gen_parameter_tlv(struct ibuf *, struct eigrp_iface *,
diff --git a/packet.c b/packet.c
index 792d577..1e53030 100644
--- a/packet.c
+++ b/packet.c
@@ -34,7 +34,6 @@
 
 extern struct eigrpd_conf	*econf;
 
-int		 ip_hdr_sanity_check(const struct ip *, uint16_t);
 int		 eigrp_hdr_sanity_check(int, union eigrpd_addr *,
 		    struct eigrp_hdr *, uint16_t, const struct iface *);
 struct iface	*find_iface(unsigned int, int, union eigrpd_addr *);
@@ -305,7 +304,7 @@ recv_packet_nbr(struct nbr *nbr, struct eigrp_hdr *eigrp_hdr,
 }
 
 static void
-recv_packet(int af, union eigrpd_addr *src, union eigrpd_addr *dest,
+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;
@@ -377,7 +376,7 @@ recv_packet(int af, union eigrpd_addr *src, union eigrpd_addr *dest,
 			if (tlv_decode_route(af, &tlv, buf, &ri) < 0)
 				goto error;
 			if ((re = calloc(1, sizeof(*re))) == NULL)
-				fatal("recv_packet");
+				fatal("recv_packet_eigrp");
 			re->rinfo = ri;
 			TAILQ_INSERT_TAIL(&rinfo_list, re, entry);
 			break;
@@ -459,25 +458,27 @@ recv_packet(int af, union eigrpd_addr *src, union eigrpd_addr *dest,
 	seq_addr_list_clr(&seq_addr_list);
 }
 
+#define CMSG_MAXLEN max(sizeof(struct sockaddr_dl), sizeof(struct in6_pktinfo))
 void
-recv_packet_v4(int fd, short event, void *bula)
+recv_packet(int fd, short event, void *bula)
 {
 	union {
 		struct	cmsghdr hdr;
-		char	buf[CMSG_SPACE(sizeof(struct sockaddr_dl))];
+		char	buf[CMSG_SPACE(CMSG_MAXLEN)];
 	} cmsgbuf;
 	struct msghdr		 msg;
+	struct sockaddr_storage	 from;
 	struct iovec		 iov;
 	struct ip		 ip_hdr;
-	struct eigrp_hdr	*eigrp_hdr;
-	struct iface		*iface;
 	char			*buf;
 	struct cmsghdr		*cmsg;
 	ssize_t			 r;
 	uint16_t		 len;
-	int			 l;
-	unsigned int		 ifindex = 0;
+	int			 af;
 	union eigrpd_addr	 src, dest;
+	unsigned int		 ifindex = 0;
+	struct iface		*iface;
+	struct eigrp_hdr	*eigrp_hdr;
 
 	if (event != EV_READ)
 		return;
@@ -486,6 +487,8 @@ recv_packet_v4(int fd, short event, void *bula)
 	memset(&msg, 0, sizeof(msg));
 	iov.iov_base = buf = pkt_ptr;
 	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;
@@ -497,109 +500,24 @@ recv_packet_v4(int fd, short event, void *bula)
 			    strerror(errno));
 		return;
 	}
-	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
-	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
-		if (cmsg->cmsg_level == IPPROTO_IP &&
-		    cmsg->cmsg_type == IP_RECVIF) {
-			ifindex = ((struct sockaddr_dl *)
-			    CMSG_DATA(cmsg))->sdl_index;
-			break;
-		}
-	}
-
 	len = (uint16_t)r;
 
-	/* IP header sanity checks */
-	if (len < sizeof(ip_hdr)) {
-		log_debug("%s: bad packet size", __func__);
-		return;
-	}
-	memcpy(&ip_hdr, buf, sizeof(ip_hdr));
-	if ((l = ip_hdr_sanity_check(&ip_hdr, len)) == -1)
-		return;
-	buf += l;
-	len -= l;
-
-	src.v4 = ip_hdr.ip_src;
-	dest.v4 = ip_hdr.ip_dst;
-
-	/* find a matching interface */
-	if ((iface = find_iface(ifindex, AF_INET, &src)) == NULL)
-		return;
-
-	/*
-	 * Packet needs to be sent to AllEIGRPRouters_v4 or to one
-	 * of the interface addresses.
-	 */
-	if (ip_hdr.ip_dst.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 &&
-			    ip_hdr.ip_dst.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(ip_hdr.ip_dst),
-			    iface->name);
-			return;
-		}
-	}
-
-	if (len < sizeof(*eigrp_hdr)) {
-		log_debug("%s: bad packet size", __func__);
+	sa2addr((struct sockaddr *)&from, &af, &src);
+	if (bad_addr(af, &src)) {
+		log_debug("%s: invalid source address: %s", __func__,
+		    log_addr(af, &src));
 		return;
 	}
-	eigrp_hdr = (struct eigrp_hdr *)buf;
-
-	recv_packet(AF_INET, &src, &dest, iface, eigrp_hdr, buf, len);
-}
-
-void
-recv_packet_v6(int fd, short event, void *bula)
-{
-	union {
-		struct	cmsghdr hdr;
-		char	buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
-	} cmsgbuf;
-	struct msghdr		 msg;
-	struct iovec		 iov;
-	struct sockaddr_in6	 sin6;
-	struct eigrp_hdr	*eigrp_hdr;
-	struct iface		*iface;
-	char			*buf;
-	struct cmsghdr		*cmsg;
-	ssize_t			 r;
-	uint16_t		 len;
-	unsigned int		 ifindex = 0;
-	union eigrpd_addr	 src, dest;
-
-	if (event != EV_READ)
-		return;
-
-	/* setup buffer */
-	memset(&msg, 0, sizeof(msg));
-	iov.iov_base = buf = pkt_ptr;
-	iov.iov_len = READ_BUF_SIZE;
-	msg.msg_name = &sin6;
-	msg.msg_namelen = sizeof(sin6);
-	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;
-	}
 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
 	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
-		if (cmsg->cmsg_level == IPPROTO_IPV6 &&
+		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;
@@ -608,61 +526,78 @@ recv_packet_v6(int fd, short event, void *bula)
 			break;
 		}
 	}
-	src.v6 = sin6.sin6_addr;
-
-	/* validate source address */
-	if (bad_addr_v6(&src.v6)) {
-		log_debug("%s: invalid source address: %s", __func__,
-		    log_addr(AF_INET, &src));
-		return;
-	}
 
 	/* find a matching interface */
-	if ((iface = find_iface(ifindex, AF_INET6, &src)) == NULL)
+	if ((iface = find_iface(ifindex, af, &src)) == NULL)
 		return;
 
-	/*
-	 * Packet needs to be sent to AllEIGRPRouters_v6 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;
+	/* 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;
 	}
 
-	len = (uint16_t)r;
 	if (len < sizeof(*eigrp_hdr)) {
 		log_debug("%s: bad packet size", __func__);
 		return;
 	}
 	eigrp_hdr = (struct eigrp_hdr *)buf;
 
-	recv_packet(AF_INET6, &src, &dest, iface, eigrp_hdr, buf, len);
-}
-
-int
-ip_hdr_sanity_check(const struct ip *ip_hdr, uint16_t len)
-{
-	if (ntohs(ip_hdr->ip_len) != len) {
-		log_debug("%s: invalid IP packet length %u", __func__,
-		    ntohs(ip_hdr->ip_len));
-		return (-1);
-	}
-
-	/* validate source address */
-	if (bad_addr_v4(ip_hdr->ip_src)) {
-		log_debug("%s: invalid source address: %s", __func__,
-		    inet_ntoa(ip_hdr->ip_src));
-		return (-1);
-	}
-
-	if (ip_hdr->ip_p != IPPROTO_EIGRP)
-		/* this is enforced by the socket itself */
-		fatalx("ip_hdr_sanity_check: invalid IP proto");
-
-	return (ip_hdr->ip_hl << 2);
+	recv_packet_eigrp(af, &src, &dest, iface, eigrp_hdr, buf, len);
 }
 
 int
diff --git a/util.c b/util.c
index 987a064..91f2b58 100644
--- a/util.c
+++ b/util.c
@@ -306,3 +306,24 @@ clearscope(struct in6_addr *in6)
 		in6->s6_addr[3] = 0;
 	}
 }
+
+void
+sa2addr(struct sockaddr *sa, int *af, union eigrpd_addr *addr)
+{
+	struct sockaddr_in		*sa_in = (struct sockaddr_in *)sa;
+	struct sockaddr_in6		*sa_in6 = (struct sockaddr_in6 *)sa;
+
+	memset(addr, 0, sizeof(*addr));
+	switch (sa->sa_family) {
+	case AF_INET:
+		*af = AF_INET;
+		addr->v4 = sa_in->sin_addr;
+		break;
+	case AF_INET6:
+		*af = AF_INET6;
+		addr->v6 = sa_in6->sin6_addr;
+		break;
+	default:
+		fatalx("sa2addr: unknown af");
+	}
+}