-/* $OpenBSD: bpf.c,v 1.17 2017/02/13 22:49:38 krw Exp $ */
+/* $OpenBSD: bpf.c,v 1.18 2017/04/05 14:40:56 reyk Exp $ */
/* BPF socket interface code, originally contributed by Archie Cobbs. */
{
unsigned char buf[256];
struct iovec iov[2];
- int result, bufp = 0;
+ ssize_t bufp;
+ int result;
+
+ result = -1;
if (interface->hw_address.htype == HTYPE_IPSEC_TUNNEL) {
socklen_t slen = pc->pc_dst.ss_len;
}
/* Assemble the headers... */
- assemble_hw_header(interface, buf, &bufp, pc);
- assemble_udp_ip_header(interface, buf, &bufp, pc,
- (unsigned char *)raw, len);
+ if ((bufp = assemble_hw_header(buf, sizeof(buf), 0, pc,
+ interface->hw_address.htype)) == -1)
+ goto done;
+ if ((bufp = assemble_udp_ip_header(buf, sizeof(buf), bufp, pc,
+ (unsigned char *)raw, len)) == -1)
+ goto done;
/* Fire it off */
iov[0].iov_base = (char *)buf;
receive_packet(struct interface_info *interface, unsigned char *buf,
size_t len, struct packet_ctx *pc)
{
- int length = 0, offset = 0;
+ int length = 0;
+ ssize_t offset = 0;
struct bpf_hdr hdr;
/*
interface->rbuf_offset += hdr.bh_hdrlen;
/* Decode the physical header... */
- offset = decode_hw_header(interface,
- interface->rbuf, interface->rbuf_offset, pc);
+ offset = decode_hw_header(interface->rbuf,
+ interface->rbuf_len, interface->rbuf_offset, pc,
+ interface->hw_address.htype);
/*
- * If a physical layer checksum failed (dunno of any
- * physical layer that supports this, but WTH), skip
- * this packet.
+ * If decoding or a physical layer checksum failed
+ * (dunno of any physical layer that supports this, but WTH),
+ * skip this packet.
*/
if (offset < 0) {
interface->rbuf_offset += hdr.bh_caplen;
continue;
}
- interface->rbuf_offset += offset;
- hdr.bh_caplen -= offset;
/* Decode the IP and UDP headers... */
- offset = decode_udp_ip_header(interface, interface->rbuf,
- interface->rbuf_offset, pc, hdr.bh_caplen);
+ offset = decode_udp_ip_header(interface->rbuf,
+ interface->rbuf_len, offset, pc);
/* If the IP or UDP checksum was bad, skip the packet... */
if (offset < 0) {
interface->rbuf_offset += hdr.bh_caplen;
continue;
}
- interface->rbuf_offset += offset;
- hdr.bh_caplen -= offset;
+
+ hdr.bh_caplen -= offset - interface->rbuf_offset;
+ interface->rbuf_offset = offset;
/*
* If there's not enough room to stash the packet data,
-/* $OpenBSD: packet.c,v 1.13 2017/02/13 19:15:39 krw Exp $ */
+/* $OpenBSD: packet.c,v 1.14 2017/04/05 14:40:56 reyk Exp $ */
/* Packet assembly code, originally contributed by Archie Cobbs. */
#include "log.h"
-u_int32_t checksum(unsigned char *, unsigned, u_int32_t);
+u_int32_t checksum(unsigned char *, u_int32_t, u_int32_t);
u_int32_t wrapsum(u_int32_t);
u_int32_t
-checksum(unsigned char *buf, unsigned nbytes, u_int32_t sum)
+checksum(unsigned char *buf, u_int32_t nbytes, u_int32_t sum)
{
- int i;
+ u_int32_t i;
/* Checksum all the pairs of bytes first... */
for (i = 0; i < (nbytes & ~1U); i += 2) {
return (htons(sum));
}
-void
-assemble_hw_header(struct interface_info *interface, unsigned char *buf,
- int *bufix, struct packet_ctx *pc)
+ssize_t
+assemble_hw_header(unsigned char *buf, size_t buflen,
+ size_t offset, struct packet_ctx *pc, unsigned int intfhtype)
{
struct ether_header eh;
- /* Use the supplied address or let the kernel fill it. */
- memcpy(eh.ether_shost, pc->pc_smac, ETHER_ADDR_LEN);
- memcpy(eh.ether_dhost, pc->pc_dmac, ETHER_ADDR_LEN);
+ switch (intfhtype) {
+ case HTYPE_ETHER:
+ if (buflen < offset + ETHER_HDR_LEN)
+ return (-1);
+
+ /* Use the supplied address or let the kernel fill it. */
+ memcpy(eh.ether_shost, pc->pc_smac, ETHER_ADDR_LEN);
+ memcpy(eh.ether_dhost, pc->pc_dmac, ETHER_ADDR_LEN);
- eh.ether_type = htons(ETHERTYPE_IP);
+ eh.ether_type = htons(ETHERTYPE_IP);
+
+ memcpy(&buf[offset], &eh, ETHER_HDR_LEN);
+ offset += ETHER_HDR_LEN;
+ break;
+ default:
+ return (-1);
+ }
- memcpy(&buf[*bufix], &eh, ETHER_HDR_LEN);
- *bufix += ETHER_HDR_LEN;
+ return (offset);
}
-void
-assemble_udp_ip_header(struct interface_info *interface, unsigned char *buf,
- int *bufix, struct packet_ctx *pc, unsigned char *data, int len)
+ssize_t
+assemble_udp_ip_header(unsigned char *buf, size_t buflen, size_t offset,
+ struct packet_ctx *pc, unsigned char *data, size_t datalen)
{
struct ip ip;
struct udphdr udp;
+ if (buflen < offset + sizeof(ip) + sizeof(udp))
+ return (-1);
+
ip.ip_v = 4;
ip.ip_hl = 5;
ip.ip_tos = IPTOS_LOWDELAY;
- ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len);
+ ip.ip_len = htons(sizeof(ip) + sizeof(udp) + datalen);
ip.ip_id = 0;
ip.ip_off = 0;
ip.ip_ttl = 16;
ip.ip_dst.s_addr = ss2sin(&pc->pc_dst)->sin_addr.s_addr;
ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0));
- memcpy(&buf[*bufix], &ip, sizeof(ip));
- *bufix += sizeof(ip);
+ memcpy(&buf[offset], &ip, sizeof(ip));
+ offset += sizeof(ip);
udp.uh_sport = ss2sin(&pc->pc_src)->sin_port;
udp.uh_dport = ss2sin(&pc->pc_dst)->sin_port;
- udp.uh_ulen = htons(sizeof(udp) + len);
+ udp.uh_ulen = htons(sizeof(udp) + datalen);
memset(&udp.uh_sum, 0, sizeof(udp.uh_sum));
udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
- checksum(data, len, checksum((unsigned char *)&ip.ip_src,
+ checksum(data, datalen, checksum((unsigned char *)&ip.ip_src,
2 * sizeof(ip.ip_src),
IPPROTO_UDP + (u_int32_t)ntohs(udp.uh_ulen)))));
- memcpy(&buf[*bufix], &udp, sizeof(udp));
- *bufix += sizeof(udp);
+ memcpy(&buf[offset], &udp, sizeof(udp));
+ offset += sizeof(udp);
+
+ return (offset);
}
ssize_t
-decode_hw_header(struct interface_info *interface, unsigned char *buf,
- int bufix, struct packet_ctx *pc)
+decode_hw_header(unsigned char *buf, size_t buflen,
+ size_t offset, struct packet_ctx *pc, unsigned int intfhtype)
{
- size_t offset = 0;
+ u_int32_t ip_len;
+ struct ip *ip;
- if (interface->hw_address.htype == HTYPE_IPSEC_TUNNEL) {
- u_int32_t ip_len;
- struct ip *ip;
+ switch (intfhtype) {
+ case HTYPE_IPSEC_TUNNEL:
+ if (buflen < offset + ENC_HDRLEN + sizeof(*ip))
+ return (-1);
+ offset += ENC_HDRLEN;
+ ip_len = (buf[offset] & 0xf) << 2;
+ if (buflen < offset + ip_len)
+ return (-1);
- bufix += ENC_HDRLEN;
- ip_len = (buf[bufix] & 0xf) << 2;
- ip = (struct ip *)(buf + bufix);
+ ip = (struct ip *)(buf + offset);
/* Encapsulated IP */
if (ip->ip_p != IPPROTO_IPIP)
return (-1);
memset(pc->pc_dmac, 0xff, ETHER_ADDR_LEN);
- offset = ENC_HDRLEN + ip_len;
- } else {
- memcpy(pc->pc_dmac, buf + bufix, ETHER_ADDR_LEN);
- memcpy(pc->pc_smac, buf + bufix + ETHER_ADDR_LEN,
+ offset += ip_len;
+
+ pc->pc_htype = ARPHRD_ETHER;
+ pc->pc_hlen = ETHER_ADDR_LEN;
+ break;
+ case HTYPE_ETHER:
+ if (buflen < offset + ETHER_HDR_LEN)
+ return (-1);
+
+ memcpy(pc->pc_dmac, buf + offset, ETHER_ADDR_LEN);
+ memcpy(pc->pc_smac, buf + offset + ETHER_ADDR_LEN,
ETHER_ADDR_LEN);
- offset = sizeof(struct ether_header);
- }
+ offset += ETHER_HDR_LEN;
- pc->pc_htype = ARPHRD_ETHER;
- pc->pc_hlen = ETHER_ADDR_LEN;
+ pc->pc_htype = ARPHRD_ETHER;
+ pc->pc_hlen = ETHER_ADDR_LEN;
+ break;
+ default:
+ return (-1);
+ }
return (offset);
}
ssize_t
-decode_udp_ip_header(struct interface_info *interface, unsigned char *buf,
- int bufix, struct packet_ctx *pc, int buflen)
+decode_udp_ip_header(unsigned char *buf, size_t buflen,
+ size_t offset, struct packet_ctx *pc)
{
struct ip *ip;
struct udphdr *udp;
int len;
/* Assure that an entire IP header is within the buffer. */
- if (sizeof(*ip) > buflen)
+ if (buflen < offset + sizeof(*ip))
return (-1);
- ip_len = (buf[bufix] & 0xf) << 2;
- if (ip_len > buflen)
+ ip_len = (buf[offset] & 0xf) << 2;
+ if (buflen < offset + ip_len)
return (-1);
- ip = (struct ip *)(buf + bufix);
+
+ ip = (struct ip *)(buf + offset);
ip_packets_seen++;
/* Check the IP header checksum - it should be zero. */
- if (wrapsum(checksum(buf + bufix, ip_len, 0)) != 0) {
+ if (wrapsum(checksum(buf + offset, ip_len, 0)) != 0) {
ip_packets_bad_checksum++;
if (ip_packets_seen > 4 && ip_packets_bad_checksum != 0 &&
(ip_packets_seen / ip_packets_bad_checksum) < 2) {
sizeof(ss2sin(&pc->pc_dst)->sin_addr));
#ifdef DEBUG
- if (ntohs(ip->ip_len) != buflen)
- log_debug("ip length %d disagrees with bytes received %d.",
- ntohs(ip->ip_len), buflen);
+ if (buflen != offset + ntohs(ip->ip_len))
+ log_debug("ip length %d disagrees with bytes received %zd.",
+ ntohs(ip->ip_len), buflen - offset);
#endif
/* Assure that the entire IP packet is within the buffer. */
- if (ntohs(ip->ip_len) > buflen)
+ if (buflen < offset + ntohs(ip->ip_len))
return (-1);
/* Assure that the UDP header is within the buffer. */
- if (ip_len + sizeof(*udp) > buflen)
+ if (buflen < offset + ip_len + sizeof(*udp))
return (-1);
- udp = (struct udphdr *)(buf + bufix + ip_len);
+ udp = (struct udphdr *)(buf + offset + ip_len);
udp_packets_seen++;
/* Assure that the entire UDP packet is within the buffer. */
- if (ip_len + ntohs(udp->uh_ulen) > buflen)
+ if (buflen < offset + ip_len + ntohs(udp->uh_ulen))
return (-1);
- data = buf + bufix + ip_len + sizeof(*udp);
+ data = buf + offset + ip_len + sizeof(*udp);
/*
* Compute UDP checksums, including the ``pseudo-header'', the
*/
udp_packets_length_checked++;
len = ntohs(udp->uh_ulen) - sizeof(*udp);
- if ((len < 0) || (len + data > buf + bufix + buflen)) {
+ if ((len < 0) || (len + data > buf + buflen)) {
udp_packets_length_overflow++;
if (udp_packets_length_checked > 4 &&
udp_packets_length_overflow != 0 &&
}
return (-1);
}
- if (len + data != buf + bufix + buflen)
+ if (len + data != buf + buflen)
log_debug("accepting packet with data after udp payload.");
usum = udp->uh_sum;
ss2sin(&pc->pc_src)->sin_port = udp->uh_sport;
ss2sin(&pc->pc_dst)->sin_port = udp->uh_dport;
- return (ip_len + sizeof(*udp));
+ return (offset + ip_len + sizeof(*udp));
}