aboutsummaryrefslogtreecommitdiff
path: root/sys/netinet/ip_divert.c
diff options
context:
space:
mode:
authorSam Leffler <sam@FreeBSD.org>2003-11-08 23:09:42 +0000
committerSam Leffler <sam@FreeBSD.org>2003-11-08 23:09:42 +0000
commit252f24a2cf404a4ccfedc94b6b36fd93eda9b100 (patch)
tree1e4d42947a232924db32b3768567ea2d4914bf90 /sys/netinet/ip_divert.c
parent84843845648696c938739a935c90a76630e7bbbd (diff)
downloadsrc-252f24a2cf404a4ccfedc94b6b36fd93eda9b100.tar.gz
src-252f24a2cf404a4ccfedc94b6b36fd93eda9b100.zip
divert socket fixups:
o pickup Giant in divert_packet to protect sbappendaddr since it can be entered through MPSAFE callouts or through ip_input when mpsafenet is 1 o add missing locking on output o add locking to abort and shutdown o add a ctlinput handler to invalidate held routing table references on an ICMP redirect (may not be needed) Supported by: FreeBSD Foundation
Notes
Notes: svn path=/head/; revision=122331
Diffstat (limited to 'sys/netinet/ip_divert.c')
-rw-r--r--sys/netinet/ip_divert.c88
1 files changed, 73 insertions, 15 deletions
diff --git a/sys/netinet/ip_divert.c b/sys/netinet/ip_divert.c
index 571abb8cfe85..5b445270d6db 100644
--- a/sys/netinet/ip_divert.c
+++ b/sys/netinet/ip_divert.c
@@ -218,6 +218,20 @@ divert_packet(struct mbuf *m, int incoming, int port, int rule)
sizeof(divsrc.sin_zero));
}
+ /*
+ * XXX sbappendaddr must be protected by Giant until
+ * we have locking at the socket layer. When entered
+ * from below we come in w/o Giant and must take it
+ * here. Unfortunately we cannot tell whether we're
+ * entering from above (already holding Giant),
+ * below (potentially without Giant), or otherwise
+ * (e.g. from tcp_syncache through a timeout) so we
+ * have to grab it regardless. This causes a LOR with
+ * the tcp lock, at least, and possibly others. For
+ * the moment we're ignoring this. Once sockets are
+ * locked this cruft can be removed.
+ */
+ mtx_lock(&Giant);
/* Put packet on socket queue, if any */
sa = NULL;
nport = htons((u_int16_t)port);
@@ -239,6 +253,7 @@ divert_packet(struct mbuf *m, int incoming, int port, int rule)
INP_UNLOCK(inp);
}
INP_INFO_RUNLOCK(&divcbinfo);
+ mtx_unlock(&Giant);
if (sa == NULL) {
m_freem(m);
ipstat.ips_noproto++;
@@ -297,9 +312,12 @@ div_output(struct socket *so, struct mbuf *m,
/* Reinject packet into the system as incoming or outgoing */
if (!sin || sin->sin_addr.s_addr == 0) {
- struct inpcb *const inp = sotoinpcb(so);
struct ip *const ip = mtod(m, struct ip *);
+ struct inpcb *inp;
+ INP_INFO_WLOCK(&divcbinfo);
+ inp = sotoinpcb(so);
+ INP_LOCK(inp);
/*
* Don't allow both user specified and setsockopt options,
* and don't allow packet length sizes that will crash
@@ -307,20 +325,23 @@ div_output(struct socket *so, struct mbuf *m,
if (((ip->ip_hl != (sizeof (*ip) >> 2)) && inp->inp_options) ||
((u_short)ntohs(ip->ip_len) > m->m_pkthdr.len)) {
error = EINVAL;
- goto cantsend;
+ m_freem(m);
+ } else {
+ /* Convert fields to host order for ip_output() */
+ ip->ip_len = ntohs(ip->ip_len);
+ ip->ip_off = ntohs(ip->ip_off);
+
+ /* Send packet to output processing */
+ ipstat.ips_rawout++; /* XXX */
+
+ error = ip_output((struct mbuf *)&divert_tag,
+ inp->inp_options, &inp->inp_route,
+ (so->so_options & SO_DONTROUTE) |
+ IP_ALLOWBROADCAST | IP_RAWOUTPUT,
+ inp->inp_moptions, NULL);
}
-
- /* Convert fields to host order for ip_output() */
- ip->ip_len = ntohs(ip->ip_len);
- ip->ip_off = ntohs(ip->ip_off);
-
- /* Send packet to output processing */
- ipstat.ips_rawout++; /* XXX */
- error = ip_output((struct mbuf *)&divert_tag,
- inp->inp_options, &inp->inp_route,
- (so->so_options & SO_DONTROUTE) |
- IP_ALLOWBROADCAST | IP_RAWOUTPUT,
- inp->inp_moptions, NULL);
+ INP_UNLOCK(inp);
+ INP_INFO_WUNLOCK(&divcbinfo);
} else {
if (m->m_pkthdr.rcvif == NULL) {
/*
@@ -409,8 +430,19 @@ div_detach(struct socket *so)
static int
div_abort(struct socket *so)
{
+ struct inpcb *inp;
+
+ INP_INFO_WLOCK(&divcbinfo);
+ inp = sotoinpcb(so);
+ if (inp == 0) {
+ INP_INFO_WUNLOCK(&divcbinfo);
+ return EINVAL; /* ??? possible? panic instead? */
+ }
+ INP_LOCK(inp);
soisdisconnected(so);
- return div_detach(so);
+ in_pcbdetach(inp);
+ INP_INFO_WUNLOCK(&divcbinfo);
+ return 0;
}
static int
@@ -455,7 +487,18 @@ div_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
static int
div_shutdown(struct socket *so)
{
+ struct inpcb *inp;
+
+ INP_INFO_RLOCK(&divcbinfo);
+ inp = sotoinpcb(so);
+ if (inp == 0) {
+ INP_INFO_RUNLOCK(&divcbinfo);
+ return EINVAL;
+ }
+ INP_LOCK(inp);
+ INP_INFO_RUNLOCK(&divcbinfo);
socantsendmore(so);
+ INP_UNLOCK(inp);
return 0;
}
@@ -475,6 +518,21 @@ div_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
return div_output(so, m, (struct sockaddr_in *)nam, control);
}
+void
+div_ctlinput(int cmd, struct sockaddr *sa, void *vip)
+{
+ struct in_addr faddr;
+
+ faddr = ((struct sockaddr_in *)sa)->sin_addr;
+ if (sa->sa_family != AF_INET || faddr.s_addr == INADDR_ANY)
+ return;
+ if (PRC_IS_REDIRECT(cmd)) {
+ /* flush held routes */
+ in_pcbnotifyall(&divcbinfo, faddr,
+ inetctlerrmap[cmd], in_rtchange);
+ }
+}
+
static int
div_pcblist(SYSCTL_HANDLER_ARGS)
{