Subject: mbuf problems
To: None <tech-net@netbsd.org>
From: Emmanuel Dreyfus <manu@netbsd.org>
List: tech-net
Date: 12/08/2005 16:05:13
--sm4nu43k4a2Rpi4c
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Hello
I posted recently about the bug in NAT-T: The m_pullup call would change the
struct mbuf *m pointer in udp4_espinudp(), thus making the caller's value
invalid (and thus leading to a crash).
Here is an attempt to fix the problem, but it completely breaks NAT-T.
It seems the m_pulldown() call corrupts the packet (UDP and ESP).
Could someone knowledgable with mbuf help? Why is my approach wrong?
--
Emmanuel Dreyfus
manu@netbsd.org
--sm4nu43k4a2Rpi4c
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="nat-t.diff"
--- udp_usrreq.c 2005-12-08 17:01:16.000000000 +0100
+++ udp_usrreq.c.pulldown 2005-12-08 15:37:19.000000000 +0100
@@ -402,5 +402,10 @@
dst.sin_port = uh->uh_dport;
- n = udp4_realinput(&src, &dst, m, iphlen);
+ if ((n = udp4_realinput(&src, &dst, m, iphlen)) == -1) {
+ /* m was freed */
+ udpstat.udps_hdrops++;
+ return;
+ }
+
#ifdef INET6
if (IN_MULTICAST(ip->ip_dst.s_addr) || n == 0) {
@@ -803,9 +808,17 @@
struct sockaddr *sa = (struct sockaddr *)src;
- if (udp4_espinudp(m, off, sa, inp->inp_socket) != 0) {
- rcvcnt++;
+ switch (udp4_espinudp(m, off, sa, inp->inp_socket)) {
+ case -1: /* error, m was freed */
+ rcvcnt = -1;
+ goto bad;
+ break;
+ case 1: /* ESP over UDP, skip it */
+ rcvcnt++; /* XXX is that right? */
goto bad;
+ break;
+ case 0: /* Plain UDP */
+ default: /* unexpected */
+ break;
}
-
/* Normal UDP processing will take place */
}
@@ -1398,4 +1411,5 @@
* 1 if the packet was processed
* 0 if normal UDP processing should take place
+ * -1 an error occured and the mbuf was freeed
*/
static int
@@ -1406,11 +1420,10 @@
struct socket *so;
{
- size_t len;
caddr_t data;
struct inpcb *inp;
size_t skip = 0;
- size_t minlen;
size_t iphdrlen;
struct ip *ip;
+ struct mbuf *me;
struct mbuf *n;
struct m_tag *tag;
@@ -1419,24 +1432,18 @@
/*
- * Collapse the mbuf chain if the first mbuf is too short
- * The longest case is: UDP + non ESP marker + ESP
+ * Make sure the non ESP marker and the ESP header
+ * are in contiguous memory. This operation avoids moving
+ * mbuf m base and data up to off, so the calling function
+ * will not use a garbled pointer.
*/
- minlen = off + sizeof(u_int64_t) + sizeof(struct esp);
- if (minlen > m->m_pkthdr.len)
- minlen = m->m_pkthdr.len;
-
- if (m->m_len < minlen) {
- if ((m = m_pullup(m, minlen)) == NULL) {
- printf("udp4_espinudp: m_pullup failed\n");
- return 0;
- }
+ me = m_pulldown(m, off, sizeof(u_int64_t) + sizeof(struct esp), NULL);
+ if (me == NULL) {
+ printf("udp4_espinudp: m_pulldown failed\n");
+ return -1;
}
-
- len = m->m_len - off;
- data = mtod(m, caddr_t) + off;
- inp = sotoinpcb(so);
+ data = mtod(me, caddr_t);
/* Ignore keepalive packets */
- if ((len == 1) && (data[0] == '\xff')) {
+ if ((me->m_len == 1) && (data[0] == '\xff')) {
return 1;
}
@@ -1447,8 +1454,9 @@
* header to remove
*/
+ inp = sotoinpcb(so);
if (inp->inp_flags & INP_ESPINUDP) {
u_int32_t *st = (u_int32_t *)data;
- if ((len <= sizeof(struct esp)) || (*st == 0))
+ if ((me->m_len <= sizeof(struct esp)) || (*st == 0))
return 0; /* Normal UDP processing */
@@ -1457,8 +1465,8 @@
if (inp->inp_flags & INP_ESPINUDP_NON_IKE) {
- u_int32_t *st = (u_int32_t *)data;
+ u_int64_t *st = (u_int64_t *)data;
- if ((len <= sizeof(u_int64_t) + sizeof(struct esp))
- || ((st[0] | st[1]) != 0))
+ if ((me->m_len <= sizeof(u_int64_t) + sizeof(struct esp))
+ || (*st != 0))
return 0; /* Normal UDP processing */
@@ -1470,5 +1478,5 @@
* order everywhere in IPSEC_NAT_T code.
*/
- udphdr = (struct udphdr *)(data - skip);
+ udphdr = mtod(m, struct udphdr *);
sport = udphdr->uh_sport;
dport = udphdr->uh_dport;
--sm4nu43k4a2Rpi4c--