The diff below change the way we store the source routes of an incoming
IPv4 packet to use a m_tag rather than a single static entry. It is
based on FreeBSD's r135274 and should allow us to process various
packets with the SSRR or LSRR header option in parallel in the future.
It has already been reviewed by bluhm@ and I tested it on a custom setup,
but I'd like to hear from somebody really using source routing that it
doesn't introduce regression.
Index: share/man/man9/mbuf_tags.9
===================================================================
RCS file: /home/ncvs/src/share/man/man9/mbuf_tags.9,v
retrieving revision 1.30
diff -u -p -r1.30 mbuf_tags.9
--- share/man/man9/mbuf_tags.9 11 Jun 2013 01:01:15 -0000 1.30
+++ share/man/man9/mbuf_tags.9 12 Jun 2013 09:13:32 -0000
@@ -184,6 +184,11 @@ Used by
to reassemble IPv6 fragments.
The tag contains a
.Va struct pf_fragment_tag .
+.It PACKET_TAG_SRCROUTE
+Used by the IPv4 stack to keep track of the source route of an incoming
+IP packet, in case a protocol wants to respond over the same route.
+The tag contains a
+.Va struct ip_srcrt .
.El
.Pp
.Fn m_tag_find
Index: sys/netinet/ip_icmp.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/ip_icmp.c,v
retrieving revision 1.101
diff -u -p -r1.101 ip_icmp.c
--- sys/netinet/ip_icmp.c 5 Jun 2013 15:22:32 -0000 1.101
+++ sys/netinet/ip_icmp.c 12 Jun 2013 09:13:33 -0000
@@ -745,7 +745,7 @@ icmp_reflect(struct mbuf *m, struct mbuf
* add on any record-route or timestamp options.
*/
cp = (u_char *) (ip + 1);
- if (op && (opts = ip_srcroute()) == 0 &&
+ if (op && (opts = ip_srcroute(m)) == NULL &&
(opts = m_gethdr(M_DONTWAIT, MT_HEADER))) {
opts->m_len = sizeof(struct in_addr);
mtod(opts, struct in_addr *)->s_addr = 0;
Index: sys/netinet/ip_input.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/ip_input.c,v
retrieving revision 1.211
diff -u -p -r1.211 ip_input.c
--- sys/netinet/ip_input.c 17 May 2013 09:04:30 -0000 1.211
+++ sys/netinet/ip_input.c 12 Jun 2013 09:13:33 -0000
@@ -142,21 +142,21 @@ inet_ntoa(ina)
}
/*
- * We need to save the IP options in case a protocol wants to respond
+ * Used to save the IP options in case a protocol wants to respond
* to an incoming packet over the same route if the packet got here
* using IP source routing. This allows connection establishment and
* maintenance when the remote end is on a network that is not known
* to us.
*/
-int ip_nhops = 0;
-static struct ip_srcrt {
- struct in_addr dst; /* final destination */
- char nop; /* one NOP to align */
- char srcopt[IPOPT_OFFSET + 1]; /* OPTVAL, OLEN and OFFSET */
- struct in_addr route[MAX_IPOPTLEN/sizeof(struct in_addr)];
-} ip_srcrt;
+struct ip_srcrt {
+ int isr_nhops; /* number of hops */
+ struct in_addr isr_dst; /* final destination */
+ char isr_nop; /* one NOP to align */
+ char isr_hdr[IPOPT_OFFSET + 1]; /* OPTVAL, OLEN & OFFSET */
+ struct in_addr isr_routes[MAX_IPOPTLEN/sizeof(struct in_addr)];
+};
-void save_rte(u_char *, struct in_addr);
+void save_rte(struct mbuf *, u_char *, struct in_addr);
int ip_weadvertise(u_int32_t, u_int);
/*
@@ -364,7 +364,6 @@ ipv4_input(struct mbuf *m)
* error was detected (causing an icmp message
* to be sent and the original packet to be freed).
*/
- ip_nhops = 0; /* for source routed packets */
if (hlen > sizeof (struct ip) && ip_dooptions(m)) {
return;
}
@@ -1074,7 +1073,7 @@ ip_dooptions(struct mbuf *m)
/*
* End of source route. Should be for us.
*/
- save_rte(cp, ip->ip_src);
+ save_rte(m, cp, ip->ip_src);
break;
}
@@ -1243,20 +1242,29 @@ ip_rtaddr(struct in_addr dst, u_int rtab
* to be picked up later by ip_srcroute if the receiver is interested.
*/
void
-save_rte(u_char *option, struct in_addr dst)
+save_rte(struct mbuf *m, u_char *option, struct in_addr dst)
{
+ struct ip_srcrt *isr;
+ struct m_tag *mtag;
unsigned olen;
olen = option[IPOPT_OLEN];
#ifdef DIAGNOSTIC
if (ipprintfs)
- printf("save_rte: olen %d\n", olen);
-#endif /* 0 */
- if (olen > sizeof(ip_srcrt) - (1 + sizeof(dst)))
+ printf("%s: olen %d\n", __func__, olen);
+#endif
+ if (olen > sizeof(isr->isr_hdr) + sizeof(isr->isr_routes))
return;
- bcopy((caddr_t)option, (caddr_t)ip_srcrt.srcopt, olen);
- ip_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr);
- ip_srcrt.dst = dst;
+
+ mtag = m_tag_get(PACKET_TAG_SRCROUTE, sizeof(*isr), M_NOWAIT);
+ if (mtag == NULL)
+ return;
+ isr = (struct ip_srcrt *)(mtag + 1);
+
+ bcopy(option, isr->isr_hdr, olen);
+ isr->isr_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr);
+ isr->isr_dst = dst;
+ m_tag_prepend(m, mtag);
}
/*
@@ -1312,31 +1320,41 @@ ip_weadvertise(u_int32_t addr, u_int rta
* The first hop is placed before the options, will be removed later.
*/
struct mbuf *
-ip_srcroute(void)
+ip_srcroute(struct mbuf *m0)
{
struct in_addr *p, *q;
struct mbuf *m;
+ struct ip_srcrt *isr;
+ struct m_tag *mtag;
- if (ip_nhops == 0)
- return ((struct mbuf *)0);
+ if (!ip_dosourceroute)
+ return (NULL);
+
+ mtag = m_tag_find(m0, PACKET_TAG_SRCROUTE, NULL);
+ if (mtag == NULL)
+ return (NULL);
+ isr = (struct ip_srcrt *)(mtag + 1);
+
+ if (isr->isr_nhops == 0)
+ return (NULL);
m = m_get(M_DONTWAIT, MT_SOOPTS);
- if (m == 0)
- return ((struct mbuf *)0);
+ if (m == NULL)
+ return (NULL);
-#define OPTSIZ (sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt))
+#define OPTSIZ (sizeof(isr->isr_nop) + sizeof(isr->isr_hdr))
- /* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */
- m->m_len = ip_nhops * sizeof(struct in_addr) + sizeof(struct in_addr) +
- OPTSIZ;
+ /* length is (nhops+1)*sizeof(addr) + sizeof(nop + header) */
+ m->m_len = (isr->isr_nhops + 1) * sizeof(struct in_addr) + OPTSIZ;
#ifdef DIAGNOSTIC
if (ipprintfs)
- printf("ip_srcroute: nhops %d mlen %d", ip_nhops, m->m_len);
+ printf("%s: nhops %d mlen %d", __func__, isr->isr_nhops,
+ m->m_len);
#endif
/*
* First save first hop for return route
*/
- p = &ip_srcrt.route[ip_nhops - 1];
+ p = &(isr->isr_routes[isr->isr_nhops - 1]);
*(mtod(m, struct in_addr *)) = *p--;
#ifdef DIAGNOSTIC
if (ipprintfs)
@@ -1346,10 +1364,9 @@ ip_srcroute(void)
/*
* Copy option fields and padding (nop) to mbuf.
*/
- ip_srcrt.nop = IPOPT_NOP;
- ip_srcrt.srcopt[IPOPT_OFFSET] = IPOPT_MINOFF;
- bcopy((caddr_t)&ip_srcrt.nop,
- mtod(m, caddr_t) + sizeof(struct in_addr), OPTSIZ);
+ isr->isr_nop = IPOPT_NOP;
+ isr->isr_hdr[IPOPT_OFFSET] = IPOPT_MINOFF;
+ bcopy(&isr->isr_nop, mtod(m, caddr_t) + sizeof(struct in_addr), OPTSIZ);
q = (struct in_addr *)(mtod(m, caddr_t) +
sizeof(struct in_addr) + OPTSIZ);
#undef OPTSIZ
@@ -1357,7 +1374,7 @@ ip_srcroute(void)
* Record return path as an IP source route,
* reversing the path (pointers are now aligned).
*/
- while (p >= ip_srcrt.route) {
+ while (p >= isr->isr_routes) {
#ifdef DIAGNOSTIC
if (ipprintfs)
printf(" %x", ntohl(q->s_addr));
@@ -1367,11 +1384,12 @@ ip_srcroute(void)
/*
* Last hop goes to final destination.
*/
- *q = ip_srcrt.dst;
+ *q = isr->isr_dst;
#ifdef DIAGNOSTIC
if (ipprintfs)
printf(" %x\n", ntohl(q->s_addr));
#endif
+ m_tag_delete(m0, (struct m_tag *)isr);
return (m);
}
@@ -1731,7 +1749,7 @@ ip_savecontrol(struct inpcb *inp, struct
}
/* ip_srcroute doesn't do what we want here, need to fix */
if (inp->inp_flags & INP_RECVRETOPTS) {
- *mp = sbcreatecontrol((caddr_t) ip_srcroute(),
+ *mp = sbcreatecontrol((caddr_t) ip_srcroute(m),
sizeof(struct in_addr), IP_RECVRETOPTS, IPPROTO_IP);
if (*mp)
mp = &(*mp)->m_next;
Index: sys/netinet/ip_var.h
===================================================================
RCS file: /home/ncvs/src/sys/netinet/ip_var.h,v
retrieving revision 1.45
diff -u -p -r1.45 ip_var.h
--- sys/netinet/ip_var.h 10 Apr 2013 08:50:59 -0000 1.45
+++ sys/netinet/ip_var.h 12 Jun 2013 09:13:33 -0000
@@ -196,7 +196,7 @@ u_int16_t
int ip_setmoptions(int, struct ip_moptions **, struct mbuf *, u_int);
void ip_slowtimo(void);
struct mbuf *
- ip_srcroute(void);
+ ip_srcroute(struct mbuf *);
void ip_stripoptions(struct mbuf *, struct mbuf *);
int ip_sysctl(int *, u_int, void *, size_t *, void *, size_t);
void ip_savecontrol(struct inpcb *, struct mbuf **, struct ip *,
Index: sys/netinet/tcp_input.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/tcp_input.c,v
retrieving revision 1.263
diff -u -p -r1.263 tcp_input.c
--- sys/netinet/tcp_input.c 9 Jun 2013 22:03:06 -0000 1.263
+++ sys/netinet/tcp_input.c 12 Jun 2013 09:13:33 -0000
@@ -3773,7 +3773,7 @@ syn_cache_get(struct sockaddr *src, stru
case AF_INET:
inp->inp_laddr = ((struct sockaddr_in *)dst)->sin_addr;
- inp->inp_options = ip_srcroute();
+ inp->inp_options = ip_srcroute(m);
if (inp->inp_options == NULL) {
inp->inp_options = sc->sc_ipopts;
sc->sc_ipopts = NULL;
@@ -4048,7 +4048,7 @@ syn_cache_add(struct sockaddr *src, stru
/*
* Remember the IP options, if any.
*/
- ipopts = ip_srcroute();
+ ipopts = ip_srcroute(m);
break;
#endif
default:
Index: sys/sys/mbuf.h
===================================================================
RCS file: /home/ncvs/src/sys/sys/mbuf.h,v
retrieving revision 1.164
diff -u -p -r1.164 mbuf.h
--- sys/sys/mbuf.h 3 Jun 2013 16:57:06 -0000 1.164
+++ sys/sys/mbuf.h 12 Jun 2013 09:13:33 -0000
@@ -454,6 +454,7 @@ struct m_tag *m_tag_next(struct mbuf *,
#define PACKET_TAG_PF_DIVERT 0x0200 /* pf(4) diverted packet */
#define PACKET_TAG_PIPEX 0x0400 /* pipex session cache */
#define PACKET_TAG_PF_REASSEMBLED 0x0800 /* pf reassembled ipv6 packet */
+#define PACKET_TAG_SRCROUTE 0x1000 /* IPv4 source routing options */
/*
* Maximum tag payload length (that is excluding the m_tag structure).
@@ -461,7 +462,7 @@ struct m_tag *m_tag_next(struct mbuf *,
* length for an existing packet tag type or when adding a new one that
* has payload larger than the value below.
*/
-#define PACKET_TAG_MAXSIZE 40
+#define PACKET_TAG_MAXSIZE 52
#endif /* _KERNEL */
#endif /* _SYS_MBUF_H_ */