Index: iputils-20210202/arping.c
===================================================================
--- iputils-20210202.orig/arping.c
+++ iputils-20210202/arping.c
@@ -14,6 +14,7 @@
 #include <errno.h>
 #include <ifaddrs.h>
 #include <linux/if_ether.h>
+#include <linux/if_link.h>
 #include <linux/if_packet.h>
 #include <linux/rtnetlink.h>
 #include <netdb.h>
@@ -49,6 +50,7 @@
 struct device {
 	char *name;
 	int ifindex;
+	uint16_t vlan_tag;
 	struct ifaddrs *ifa;
 };
 
@@ -85,12 +87,28 @@ struct run_state {
 		quiet:1,
 		quit_on_reply:1,
 		unicasting:1,
-		unsolicited:1;
+		unsolicited:1,
+		senduntagged:1,
+		untagged:1,
+		sockraw:1;
+	uint16_t tagged;
 };
 
 #ifdef HAVE_LIBCAP
 static const cap_value_t caps[] = { CAP_NET_RAW };
 #endif
+#ifndef __UAPI_DEF_ETHHDR
+struct ethhdr {
+	unsigned char   h_dest[ETH_ALEN];
+	unsigned char   h_source[ETH_ALEN];
+	__be16          h_proto;
+};
+#endif
+
+struct vlan_hdr {
+	__be16	h_vlan_tci;
+	__be16	h_proto;
+};
 
 /*
  * All includes, definitions, struct declarations, and global variables are
@@ -120,7 +138,11 @@ static void usage(void)
 		"  -U            unsolicited ARP mode, update your neighbours\n"
 		"  -A            ARP answer mode, update your neighbours\n"
 		"  -V            print version and exit\n"
+		"  -t            accept any 802.1q tagged packets\n"
+		"  -T <vlan-id>  which 802.1q tag to accept and send\n"
+		"  -u            with -T, dont send tagged packets\n"
 		"  -c <count>    how many packets to send\n"
+		"  -C <count>    how many packets to receive (1 == -f)\n"
 		"  -w <timeout>  how long to wait for a reply\n"
 		"  -i <interval> set interval between packets (default: 1 second)\n"
 		"  -I <device>   which ethernet device to use"
@@ -137,6 +159,143 @@ static void usage(void)
 	exit(2);
 }
 
+static int netlink_parse_rta_vlan_info(struct rtattr * attrhd, ssize_t len, uint16_t *data)
+{
+	struct rtattr * attr = attrhd;
+	ssize_t attr_len = len;
+	uint16_t vlan_data = 0;
+	int got_ifla_li = 0;
+	int got_ifla_data = 0;
+	int got_ifla_vlan = 0;
+
+	if (!attr || !data)
+		return -1;
+
+	for (; RTA_OK(attr, attr_len); attr = RTA_NEXT(attr, attr_len)) {
+		if (attr->rta_type == IFLA_LINKINFO) {
+			attr_len = RTA_PAYLOAD(attr);
+			attr = RTA_DATA(attr);
+			got_ifla_li = 1;
+			continue;
+		}
+		if (got_ifla_li && attr->rta_type == IFLA_INFO_DATA) {
+			attr_len = RTA_PAYLOAD(attr);
+			attr = RTA_DATA(attr);
+			got_ifla_data = 1;
+			continue;
+		}
+		if (got_ifla_data && attr->rta_type == IFLA_VLAN_ID) {
+			vlan_data = *(uint16_t *) RTA_DATA(attr);
+			got_ifla_vlan = 1;
+			break;
+		}
+	}
+	if (got_ifla_vlan) {
+		*data = vlan_data;
+		return 0;
+	}
+	return -1;
+}
+
+#define DEVINFO_QUERY_TYPE_VLAN 0x01
+#define DEVINFO_QUERY_TYPE_MAX 0xffff
+
+#define DEVINFO_QUERY_INFO_IFINDEX 0x01
+
+static void netlink_devinfo_query(struct run_state *const ctl, void *info, int itype, void *data, int dtype)
+{
+	struct sockaddr_nl src_addr = { .nl_family = AF_NETLINK, .nl_pid = (__u32) getpid()};
+	struct sockaddr_nl dest_addr = {.nl_family = AF_NETLINK};
+	int optval = 1;
+	struct {
+		struct nlmsghdr nh;
+		struct ifinfomsg ifi;
+	} request = {0};
+	char *bp = NULL;
+	char buffer[8192];
+	struct iovec iov = {0};
+	struct msghdr msg = {0};
+	struct nlmsghdr *nh;
+	struct ifinfomsg *ifi;
+	struct rtattr *attr;
+	ssize_t n, attr_len;
+	uint16_t vlan_data = 0;
+
+	if (!ctl && (!info || !data || dtype < 1))
+		return;
+
+	int request_fd;
+
+	request.nh.nlmsg_len = NLMSG_LENGTH(sizeof(request.ifi));
+	request.nh.nlmsg_type = RTM_GETLINK;
+	request.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP_FILTERED;
+	request.ifi.ifi_family = AF_NETLINK;
+
+	if (itype & DEVINFO_QUERY_INFO_IFINDEX)
+		request.ifi.ifi_index = *((int *)info);
+      	else if (ctl)
+		request.ifi.ifi_index = (int) if_nametoindex(ctl->device.name);
+	else
+		return;
+
+	request_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+	if (request_fd == -1)
+		error(2, errno, "Netlink socket create failed");
+	if (setsockopt(request_fd, SOL_NETLINK, NETLINK_GET_STRICT_CHK, &optval, sizeof(optval)) < 0)
+		error(2, errno, "Netlink set socket option \"NETLINK_GET_STRICT_CHK\" failed");
+	if (bind(request_fd, (struct sockaddr *) &src_addr, sizeof(src_addr)) < 0)
+		error(2, errno, "Netlink socket bind failed");
+	iov.iov_base = &request;
+	iov.iov_len = request.nh.nlmsg_len;
+	msg.msg_name = &dest_addr;
+	msg.msg_namelen = sizeof(dest_addr);
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+
+	if (sendmsg(request_fd, (struct msghdr *) &msg, 0) < 0)
+		error(2, errno, "Error sending interface request to netlink");
+	memset(&iov, 0, sizeof(iov));
+	iov.iov_base = buffer;
+	iov.iov_len = sizeof(buffer);
+
+	n = recvmsg(request_fd, &msg, MSG_PEEK | MSG_TRUNC);
+	if (n > (ssize_t) sizeof(buffer)) {
+		bp = malloc(n+1024);
+		iov.iov_base = bp;
+		iov.iov_len = n + 1024;
+	}
+	if (n > -1)
+		n = recvmsg(request_fd, &msg, 0);
+
+	if (n < 0) {
+		if (bp)
+			free(bp);
+		error(2, errno, "Error receiving message from netlink");
+	}
+	for (nh = (struct nlmsghdr *) buffer; NLMSG_OK(nh, n); nh = NLMSG_NEXT(nh, n)) {
+		if (nh->nlmsg_type == RTM_NEWLINK) {
+			ifi = (struct ifinfomsg *) NLMSG_DATA(nh);
+			attr_len = nh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
+			attr = IFLA_RTA(ifi);
+
+			if (ctl || (dtype & DEVINFO_QUERY_TYPE_VLAN && !vlan_data))
+			{
+				if(netlink_parse_rta_vlan_info(attr, attr_len, &vlan_data) == 0)
+					if (data)
+						*((uint16_t *)data) = vlan_data;
+			}
+		}
+	}
+	if (ctl && vlan_data)
+		ctl->device.vlan_tag = vlan_data;
+	if (request_fd > -1)
+		close(request_fd);
+	if (bp)
+		free(bp);
+	return;
+}
+
+
 #ifdef HAVE_LIBCAP
 static void limit_capabilities(struct run_state *ctl)
 {
@@ -239,11 +398,32 @@ static int send_pack(struct run_state *c
 {
 	int err;
 	struct timespec now;
-	unsigned char buf[256];
+	unsigned char buf[512];
 	struct arphdr *ah = (struct arphdr *)buf;
 	unsigned char *p = (unsigned char *)(ah + 1);
+	unsigned char *bp = buf;
 	struct sockaddr_ll *ME = (struct sockaddr_ll *)&(ctl->me);
 	struct sockaddr_ll *HE = (struct sockaddr_ll *)&(ctl->he);
+	struct ethhdr *ehdr;
+	struct vlan_hdr *vhdr;
+
+	if (ctl->sockraw) {
+		ehdr = (struct ethhdr *) buf;
+		bp += sizeof(*ehdr);
+		memcpy(ehdr->h_dest, &HE->sll_addr, ETH_ALEN);
+		memcpy(ehdr->h_source, &ME->sll_addr, ETH_ALEN);
+		if (ctl->tagged && !ctl->senduntagged){
+			ehdr->h_proto = htons(ETH_P_8021Q);
+			vhdr = (struct vlan_hdr *) bp;
+			vhdr->h_vlan_tci = htons(ctl->tagged);
+			vhdr->h_proto = htons(ETH_P_ARP);
+			bp += sizeof(*vhdr);
+		}
+		else
+			ehdr->h_proto = htons(ETH_P_ARP);
+		ah = (struct arphdr *) bp;
+		p = (unsigned char *) (ah + 1);
+	}
 
 	ah->ar_hrd = htons(ME->sll_hatype);
 	if (ah->ar_hrd == htons(ARPHRD_FDDI))
@@ -697,6 +877,7 @@ static int event_loop(struct run_state *
 	uint64_t exp, total_expires = 1;
 
 	unsigned char packet[4096];
+	unsigned char *p = packet;
 	struct sockaddr_storage from;
 	memset(&from, 0, sizeof(from));
 	socklen_t addr_len = sizeof(from);
@@ -796,7 +977,84 @@ static int event_loop(struct run_state *
 				exit_loop = 1;
 				break;
 			case POLLFD_SOCKET:
-				if ((s =
+				p = packet;
+				if (ctl->tagged || !ctl->untagged) {
+					int tag_acceptable = (ctl->tagged ? 0 : 1);
+					uint16_t vlan_tag = 0;
+					uint16_t p_prot;
+					struct iovec iov = { .iov_base = packet, .iov_len = sizeof(packet) };
+					struct cmsghdr *cmsg_ptr;
+					union
+					{
+						struct cmsghdr  cmsg;
+						char            buf[CMSG_SPACE(sizeof(struct tpacket_auxdata))];
+					} cmsg_buf;
+					struct msghdr msg = {	.msg_iov = &iov,
+								.msg_iovlen = 1,
+								.msg_name = &from,
+								.msg_namelen = addr_len,
+								.msg_control = &cmsg_buf,
+								.msg_controllen = sizeof(cmsg_buf) };
+					struct tpacket_auxdata *aux_ptr;
+					struct sockaddr_ll *fp ;
+					struct ethhdr *ehdr;
+					struct vlan_hdr *vhdr;
+
+					if ((s = recvmsg(ctl->socketfd, &msg, 0)) < 0 ) {
+						error(0, errno, "recvmsg");
+						if (errno == ENETDOWN)
+							rc = 2;
+						continue;
+					}
+					fp = (struct sockaddr_ll *) &from;
+					ehdr = (struct ethhdr *) p;
+					if (ehdr->h_proto != fp->sll_protocol)
+						continue;
+					p += sizeof(*ehdr);
+					p_prot = ntohs(fp->sll_protocol);
+
+				next_eth_prot:
+					switch (p_prot) {
+						case ETH_P_8021Q:
+							vhdr = (struct vlan_hdr *) p;
+							p += sizeof(*vhdr);
+							vlan_tag = ntohs(vhdr->h_vlan_tci) & 0x0fff;
+							p_prot = ntohs(vhdr->h_proto);
+							goto next_eth_prot;
+							break;
+						case ETH_P_ARP:
+							break;
+						default:
+							continue;
+					}
+
+
+					for( cmsg_ptr = CMSG_FIRSTHDR(&msg); cmsg_ptr; cmsg_ptr = CMSG_NXTHDR( &msg, cmsg_ptr ) )
+					{
+						if( ( cmsg_ptr->cmsg_len < CMSG_LEN( sizeof( struct tpacket_auxdata ) ) ) ||
+								( cmsg_ptr->cmsg_level != SOL_PACKET ) ||
+								( cmsg_ptr->cmsg_type != PACKET_AUXDATA ) )
+							continue;
+						aux_ptr = (struct tpacket_auxdata *)CMSG_DATA(cmsg_ptr);
+						if ((aux_ptr->tp_vlan_tci & 0x0fff) != 0) /* ntohs is wrong here somehow */
+							vlan_tag = aux_ptr->tp_vlan_tci & 0x0fff;
+						else
+							continue;
+					}
+					if (vlan_tag) {
+						if(!ctl->untagged)
+							tag_acceptable = 0;
+						if (ctl->tagged && vlan_tag == ctl->tagged )
+							tag_acceptable = 1;
+						else if (ctl->device.vlan_tag == vlan_tag)
+							tag_acceptable = 1;
+						if (!tag_acceptable)
+							continue;
+					}
+					else if (ctl->tagged && !ctl->senduntagged)
+						continue;
+				}
+				else if ((s =
 				     recvfrom(ctl->socketfd, packet, sizeof(packet), 0,
 					      (struct sockaddr *)&from, &addr_len)) < 0) {
 					error(0, errno, "recvfrom");
@@ -804,8 +1062,10 @@ static int event_loop(struct run_state *
 						rc = 2;
 					continue;
 				}
+				if (p != packet && p < (packet + sizeof(packet)))
+					s -= (p - packet);
 				if (recv_pack
-				    (ctl, packet, s, (struct sockaddr_ll *)&from) == FINAL_PACKS)
+				    (ctl, p, s, (struct sockaddr_ll *)&from) == FINAL_PACKS)
 					exit_loop = 1;
 				break;
 			default:
@@ -849,7 +1109,7 @@ int main(int argc, char **argv)
 	textdomain (PACKAGE_NAME);
 #endif
 #endif
-	while ((ch = getopt(argc, argv, "h?bfDUAqc:w:i:s:I:V")) != EOF) {
+	while ((ch = getopt(argc, argv, "h?bfDuUAqc:w:i:s:I:VtT:")) != EOF) {
 		switch (ch) {
 		case 'b':
 			ctl.broadcast_only = 1;
@@ -858,6 +1118,9 @@ int main(int argc, char **argv)
 			ctl.dad = 1;
 			ctl.quit_on_reply = 1;
 			break;
+		case 'u':
+			ctl.senduntagged = 1;
+			break;
 		case 'U':
 			ctl.unsolicited = 1;
 			break;
@@ -886,6 +1149,12 @@ int main(int argc, char **argv)
 		case 's':
 			ctl.source = optarg;
 			break;
+		case 't':
+			ctl.untagged = 1;
+			break;
+		case 'T':
+			ctl.tagged = strtol_or_err(optarg, _("invalid argument"), 0, 4096);
+			break;
 		case 'V':
 			printf(IPUTILS_VERSION("arping"));
 			exit(0);
@@ -902,7 +1171,17 @@ int main(int argc, char **argv)
 		usage();
 
 	enable_capability_raw(&ctl);
-	ctl.socketfd = socket(PF_PACKET, SOCK_DGRAM, 0);
+	if (ctl.tagged) {
+		ctl.socketfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+		ctl.sockraw = 1;
+	}
+	else if (!ctl.untagged) {
+		ctl.socketfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
+		ctl.sockraw = 1;
+	}
+	else
+		ctl.socketfd = socket(PF_PACKET, SOCK_DGRAM, 0);
+
 	if (ctl.socketfd < 0)
 		error(2, errno, "socket");
 	disable_capability_raw(&ctl);
@@ -992,9 +1271,19 @@ int main(int argc, char **argv)
 		close(probe_fd);
 	};
 
+
 	((struct sockaddr_ll *)&ctl.me)->sll_family = AF_PACKET;
 	((struct sockaddr_ll *)&ctl.me)->sll_ifindex = ctl.device.ifindex;
-	((struct sockaddr_ll *)&ctl.me)->sll_protocol = htons(ETH_P_ARP);
+
+	if (ctl.tagged || !ctl.untagged) {
+		int i = 1;
+		if (setsockopt(ctl.socketfd, SOL_PACKET, PACKET_AUXDATA, &i, sizeof(i)) < 0)
+			error(2, errno, "setsockopt");
+		netlink_devinfo_query(&ctl, NULL, 0, NULL, 0);
+		((struct sockaddr_ll *)&ctl.me)->sll_protocol = htons(ETH_P_ALL);
+	}
+	if (!ctl.tagged && ctl.untagged)
+		((struct sockaddr_ll *)&ctl.me)->sll_protocol = htons(ETH_P_ARP);
 	if (bind(ctl.socketfd, (struct sockaddr *)&ctl.me, sizeof(ctl.me)) == -1)
 		error(2, errno, "bind");
 	{
