On 03/03/17(Fri) 01:47, Alexander Bluhm wrote:
> On Thu, Mar 02, 2017 at 10:55:41AM +0100, Martin Pieuchot wrote:
> > Sleeping here is completely ok. The NET_LOCK() in this function is only
> > taken to make sure no other thread will try to do a route lookup in
> > ip_output() while we're messing with the routing table.
>
> Then I think your change is ok. The kernel lock in route_input()
> should protect us. But please resend the diff after merging with
> krw@'s rtm proposal commit.
I need to refactor this spaghetti code to avoid multiple gotos. So
let's move the guts of route_output() to a function that will need
the NET_LOCK().
I'd also like to rename all functions playing with routing messages
to have the rtm_ prefix, because it is quite confusing to see more
and more rt_* popping around.
ok?
Index: net/rtsock.c
===================================================================
RCS file: /cvs/src/sys/net/rtsock.c,v
retrieving revision 1.226
diff -u -p -r1.226 rtsock.c
--- net/rtsock.c 2 Mar 2017 17:09:21 -0000 1.226
+++ net/rtsock.c 3 Mar 2017 07:52:46 -0000
@@ -103,6 +103,8 @@ void route_input(struct mbuf *m0, sa_fam
int route_arp_conflict(struct rtentry *, struct rt_addrinfo *);
int route_cleargateway(struct rtentry *, void *, unsigned int);
+int rtm_ouput(struct rt_msghdr *, struct rtentry **, struct rt_addrinfo *,
+ uint8_t, unsigned int);
struct mbuf *rt_msg1(int, struct rt_addrinfo *);
int rt_msg2(int, int, struct rt_addrinfo *, caddr_t,
struct walkarg *);
@@ -536,18 +538,13 @@ route_output(struct mbuf *m, ...)
struct rt_msghdr *rtm = NULL;
struct rtentry *rt = NULL;
struct rt_addrinfo info;
- int plen, len, seq, newgate = 0, error = 0;
- struct ifnet *ifp = NULL;
- struct ifaddr *ifa = NULL;
+ int len, seq, error = 0;
struct socket *so;
struct rawcb *rp = NULL;
-#ifdef MPLS
- struct sockaddr_mpls *psa_mpls;
-#endif
va_list ap;
u_int tableid;
- u_int8_t prio;
u_char vers, type;
+ u_int8_t prio;
va_start(ap, m);
so = va_arg(ap, struct socket *);
@@ -690,21 +687,82 @@ route_output(struct mbuf *m, ...)
error = EINVAL;
goto fail;
}
- goto flush;
+ } else {
+ error = rtm_ouput(rtm, &rt, &info, prio, tableid);
+ if (!error) {
+ type = rtm->rtm_type;
+ seq = rtm->rtm_seq;
+ free(rtm, M_RTABLE, 0);
+ rtm = rt_report(rt, type, seq, tableid);
+ }
+ }
+
+ rtfree(rt);
+ if (rtm == NULL) {
+ error = ENOBUFS;
+ goto fail;
+ } else if (error) {
+ rtm->rtm_errno = error;
+ } else {
+ rtm->rtm_flags |= RTF_DONE;
}
+ /*
+ * Check to see if we don't want our own messages.
+ */
+ if (!(so->so_options & SO_USELOOPBACK)) {
+ if (route_cb.any_count <= 1) {
+ /* no other listener and no loopback of messages */
+fail:
+ free(rtm, M_RTABLE, 0);
+ m_freem(m);
+ return (error);
+ }
+ /* There is another listener, so construct message */
+ rp = sotorawcb(so);
+ rp->rcb_proto.sp_family = 0; /* Avoid us */
+ }
+ if (rtm) {
+ if (m_copyback(m, 0, rtm->rtm_msglen, rtm, M_NOWAIT)) {
+ m_freem(m);
+ m = NULL;
+ } else if (m->m_pkthdr.len > rtm->rtm_msglen)
+ m_adj(m, rtm->rtm_msglen - m->m_pkthdr.len);
+ free(rtm, M_RTABLE, 0);
+ }
+ if (m)
+ route_input(m, info.rti_info[RTAX_DST] ?
+ info.rti_info[RTAX_DST]->sa_family : AF_UNSPEC);
+ if (rp)
+ rp->rcb_proto.sp_family = PF_ROUTE; /* Readd us */
+
+ return (error);
+}
+
+int
+rtm_ouput(struct rt_msghdr *rtm, struct rtentry **prt,
+ struct rt_addrinfo *info, uint8_t prio, unsigned int tableid)
+{
+ struct rtentry *rt = *prt;
+ struct ifnet *ifp = NULL;
+ struct ifaddr *ifa = NULL;
+#ifdef MPLS
+ struct sockaddr_mpls *psa_mpls;
+#endif
+ int plen, newgate = 0, error = 0;
+
switch (rtm->rtm_type) {
case RTM_ADD:
- if (info.rti_info[RTAX_GATEWAY] == NULL) {
+ if (info->rti_info[RTAX_GATEWAY] == NULL) {
error = EINVAL;
- goto flush;
+ break;
}
- rt = rtable_match(tableid, info.rti_info[RTAX_DST], NULL);
- if ((error = route_arp_conflict(rt, &info))) {
+ rt = rtable_match(tableid, info->rti_info[RTAX_DST], NULL);
+ if ((error = route_arp_conflict(rt, info))) {
rtfree(rt);
rt = NULL;
- goto flush;
+ break;
}
/*
@@ -718,16 +776,16 @@ route_output(struct mbuf *m, ...)
rtfree(rt);
rt = NULL;
- error = rtrequest(RTM_ADD, &info, prio, &rt, tableid);
+ error = rtrequest(RTM_ADD, info, prio, &rt, tableid);
if (error == 0)
rt_setmetrics(rtm->rtm_inits, &rtm->rtm_rmx,
&rt->rt_rmx);
else
- goto flush;
+ break;
break;
case RTM_DELETE:
- rt = rtable_lookup(tableid, info.rti_info[RTAX_DST],
- info.rti_info[RTAX_NETMASK], info.rti_info[RTAX_GATEWAY],
+ rt = rtable_lookup(tableid, info->rti_info[RTAX_DST],
+ info->rti_info[RTAX_NETMASK], info->rti_info[RTAX_GATEWAY],
prio);
/*
@@ -759,14 +817,14 @@ route_output(struct mbuf *m, ...)
rtfree(rt);
rt = NULL;
- error = rtrequest(RTM_DELETE, &info, prio, &rt, tableid);
+ error = rtrequest(RTM_DELETE, info, prio, &rt, tableid);
if (error != 0)
- goto flush;
+ break;
break;
case RTM_CHANGE:
case RTM_LOCK:
- rt = rtable_lookup(tableid, info.rti_info[RTAX_DST],
- info.rti_info[RTAX_NETMASK], info.rti_info[RTAX_GATEWAY],
+ rt = rtable_lookup(tableid, info->rti_info[RTAX_DST],
+ info->rti_info[RTAX_NETMASK], info->rti_info[RTAX_GATEWAY],
prio);
#ifndef SMALL_KERNEL
/*
@@ -774,7 +832,7 @@ route_output(struct mbuf *m, ...)
* a matching gateway.
*/
if ((rt != NULL) && ISSET(rt->rt_flags, RTF_MPATH) &&
- (info.rti_info[RTAX_GATEWAY] == NULL)) {
+ (info->rti_info[RTAX_GATEWAY] == NULL)) {
rtfree(rt);
rt = NULL;
}
@@ -783,10 +841,10 @@ route_output(struct mbuf *m, ...)
* If RTAX_GATEWAY is the argument we're trying to
* change, try to find a compatible route.
*/
- if ((rt == NULL) && (info.rti_info[RTAX_GATEWAY] != NULL) &&
+ if ((rt == NULL) && (info->rti_info[RTAX_GATEWAY] != NULL) &&
(rtm->rtm_type == RTM_CHANGE)) {
- rt = rtable_lookup(tableid, info.rti_info[RTAX_DST],
- info.rti_info[RTAX_NETMASK], NULL, prio);
+ rt = rtable_lookup(tableid, info->rti_info[RTAX_DST],
+ info->rti_info[RTAX_NETMASK], NULL, prio);
#ifndef SMALL_KERNEL
/* Ensure we don't pick a multipath one. */
if ((rt != NULL) && ISSET(rt->rt_flags, RTF_MPATH)) {
@@ -798,26 +856,26 @@ route_output(struct mbuf *m, ...)
if (rt == NULL) {
error = ESRCH;
- goto flush;
+ break;
}
/*
* RTM_CHANGE/LOCK need a perfect match.
*/
- plen = rtable_satoplen(info.rti_info[RTAX_DST]->sa_family,
- info.rti_info[RTAX_NETMASK]);
+ plen = rtable_satoplen(info->rti_info[RTAX_DST]->sa_family,
+ info->rti_info[RTAX_NETMASK]);
if (rt_plen(rt) != plen ) {
error = ESRCH;
- goto flush;
+ break;
}
switch (rtm->rtm_type) {
case RTM_CHANGE:
- if (info.rti_info[RTAX_GATEWAY] != NULL)
+ if (info->rti_info[RTAX_GATEWAY] != NULL)
if (rt->rt_gateway == NULL ||
bcmp(rt->rt_gateway,
- info.rti_info[RTAX_GATEWAY],
- info.rti_info[RTAX_GATEWAY]->sa_len)) {
+ info->rti_info[RTAX_GATEWAY],
+ info->rti_info[RTAX_GATEWAY]->sa_len)) {
newgate = 1;
}
/*
@@ -826,11 +884,11 @@ route_output(struct mbuf *m, ...)
* flags may also be different; ifp may be specified
* by ll sockaddr when protocol address is ambiguous.
*/
- if (newgate || info.rti_info[RTAX_IFP] != NULL ||
- info.rti_info[RTAX_IFA] != NULL) {
- if ((error = rt_getifa(&info, tableid)) != 0)
- goto flush;
- ifa = info.rti_ifa;
+ if (newgate || info->rti_info[RTAX_IFP] != NULL ||
+ info->rti_info[RTAX_IFA] != NULL) {
+ if ((error = rt_getifa(info, tableid)) != 0)
+ break;
+ ifa = info->rti_ifa;
if (rt->rt_ifa != ifa) {
ifp = if_get(rt->rt_ifidx);
KASSERT(ifp != NULL);
@@ -849,17 +907,17 @@ route_output(struct mbuf *m, ...)
}
}
change:
- if (info.rti_info[RTAX_GATEWAY] != NULL && (error =
- rt_setgate(rt, info.rti_info[RTAX_GATEWAY],
+ if (info->rti_info[RTAX_GATEWAY] != NULL && (error =
+ rt_setgate(rt, info->rti_info[RTAX_GATEWAY],
tableid)))
- goto flush;
+ break;
#ifdef MPLS
if ((rtm->rtm_flags & RTF_MPLS) &&
- info.rti_info[RTAX_SRC] != NULL) {
+ info->rti_info[RTAX_SRC] != NULL) {
struct rt_mpls *rt_mpls;
psa_mpls = (struct sockaddr_mpls *)
- info.rti_info[RTAX_SRC];
+ info->rti_info[RTAX_SRC];
if (rt->rt_llinfo == NULL) {
rt->rt_llinfo =
@@ -868,7 +926,7 @@ change:
}
if (rt->rt_llinfo == NULL) {
error = ENOMEM;
- goto flush;
+ break;
}
rt_mpls = (struct rt_mpls *)rt->rt_llinfo;
@@ -878,7 +936,7 @@ change:
psa_mpls->smpls_label;
}
- rt_mpls->mpls_operation = info.rti_mpls;
+ rt_mpls->mpls_operation = info->rti_mpls;
/* XXX: set experimental bits */
@@ -898,7 +956,7 @@ change:
#ifdef BFD
if (ISSET(rtm->rtm_flags, RTF_BFD)) {
if ((error = bfdset(rt)))
- goto flush;
+ break;
} else if (!ISSET(rtm->rtm_flags, RTF_BFD) &&
ISSET(rtm->rtm_fmask, RTF_BFD)) {
bfdclear(rt);
@@ -919,14 +977,14 @@ change:
ifp->if_rtrequest(ifp, RTM_ADD, rt);
if_put(ifp);
- if (info.rti_info[RTAX_LABEL] != NULL) {
+ if (info->rti_info[RTAX_LABEL] != NULL) {
char *rtlabel = ((struct sockaddr_rtlabel *)
- info.rti_info[RTAX_LABEL])->sr_label;
+ info->rti_info[RTAX_LABEL])->sr_label;
rtlabel_unref(rt->rt_labelid);
rt->rt_labelid = rtlabel_name2id(rtlabel);
}
- if_group_routechange(info.rti_info[RTAX_DST],
- info.rti_info[RTAX_NETMASK]);
+ if_group_routechange(info->rti_info[RTAX_DST],
+ info->rti_info[RTAX_NETMASK]);
/* FALLTHROUGH */
case RTM_LOCK:
rt->rt_rmx.rmx_locks &= ~(rtm->rtm_inits);
@@ -936,71 +994,17 @@ change:
}
break;
case RTM_GET:
- rt = rtable_lookup(tableid, info.rti_info[RTAX_DST],
- info.rti_info[RTAX_NETMASK], info.rti_info[RTAX_GATEWAY],
+ rt = rtable_lookup(tableid, info->rti_info[RTAX_DST],
+ info->rti_info[RTAX_NETMASK], info->rti_info[RTAX_GATEWAY],
prio);
if (rt == NULL) {
error = ESRCH;
- goto flush;
+ break;
}
break;
}
- /*
- * From here on these vars need to be valid
- * rt, rtm, error, so, m, tableid, sa_family
- *
- * Other notes:
- * - to end up here previous calls passed OK, error is most probably 0
- * - error cases take the flush route or in bad cases fail
- * - fail does not report the message back but just fails the call
- * if the message is not valid then fail should be used
- */
-
- type = rtm->rtm_type;
- seq = rtm->rtm_seq;
- free(rtm, M_RTABLE, 0);
- rtm = rt_report(rt, type, seq, tableid);
-flush:
- rtfree(rt);
- if (rtm == NULL) {
- error = ENOBUFS;
- goto fail;
- } else if (error) {
- rtm->rtm_errno = error;
- } else {
- rtm->rtm_flags |= RTF_DONE;
- }
-
- /*
- * Check to see if we don't want our own messages.
- */
- if (!(so->so_options & SO_USELOOPBACK)) {
- if (route_cb.any_count <= 1) {
- /* no other listener and no loopback of messages */
-fail:
- free(rtm, M_RTABLE, 0);
- m_freem(m);
- return (error);
- }
- /* There is another listener, so construct message */
- rp = sotorawcb(so);
- rp->rcb_proto.sp_family = 0; /* Avoid us */
- }
- if (rtm) {
- if (m_copyback(m, 0, rtm->rtm_msglen, rtm, M_NOWAIT)) {
- m_freem(m);
- m = NULL;
- } else if (m->m_pkthdr.len > rtm->rtm_msglen)
- m_adj(m, rtm->rtm_msglen - m->m_pkthdr.len);
- free(rtm, M_RTABLE, 0);
- }
- if (m)
- route_input(m, info.rti_info[RTAX_DST] ?
- info.rti_info[RTAX_DST]->sa_family : AF_UNSPEC);
- if (rp)
- rp->rcb_proto.sp_family = PF_ROUTE; /* Readd us */
-
+ *prt = rt;
return (error);
}