allow the ifa to be specified as well as the ifp on a RTM_CHANGE\;
[unix-history] / usr / src / sys / net / rtsock.c
index fd90eb8..8abe822 100644 (file)
@@ -2,19 +2,9 @@
  * Copyright (c) 1988 Regents of the University of California.
  * All rights reserved.
  *
  * Copyright (c) 1988 Regents of the University of California.
  * All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the University of California, Berkeley.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * %sccs.include.redist.c%
  *
  *
- *     @(#)rtsock.c    7.9 (Berkeley) %G%
+ *     @(#)rtsock.c    7.16 (Berkeley) %G%
  */
 
 #include "param.h"
  */
 
 #include "param.h"
@@ -34,8 +24,8 @@
 
 #include "machine/mtpr.h"
 
 
 #include "machine/mtpr.h"
 
-struct sockaddr route_dst = { 0, PF_ROUTE, };
-struct sockaddr route_src = { 0, PF_ROUTE, };
+struct sockaddr route_dst = { 2, PF_ROUTE, };
+struct sockaddr route_src = { 2, PF_ROUTE, };
 struct sockproto route_proto = { PF_ROUTE, };
 
 /*ARGSUSED*/
 struct sockproto route_proto = { PF_ROUTE, };
 
 /*ARGSUSED*/
@@ -46,6 +36,7 @@ route_usrreq(so, req, m, nam, control)
 {
        register int error = 0;
        register struct rawcb *rp = sotorawcb(so);
 {
        register int error = 0;
        register struct rawcb *rp = sotorawcb(so);
+       int s;
        if (req == PRU_ATTACH) {
                MALLOC(rp, struct rawcb *, sizeof(*rp), M_PCB, M_WAITOK);
                if (so->so_pcb = (caddr_t)rp)
        if (req == PRU_ATTACH) {
                MALLOC(rp, struct rawcb *, sizeof(*rp), M_PCB, M_WAITOK);
                if (so->so_pcb = (caddr_t)rp)
@@ -62,12 +53,14 @@ route_usrreq(so, req, m, nam, control)
                        route_cb.iso_count--;
                route_cb.any_count--;
        }
                        route_cb.iso_count--;
                route_cb.any_count--;
        }
+       s = splnet();
        error = raw_usrreq(so, req, m, nam, control);
        rp = sotorawcb(so);
        if (req == PRU_ATTACH && rp) {
                int af = rp->rcb_proto.sp_protocol;
                if (error) {
                        free((caddr_t)rp, M_PCB);
        error = raw_usrreq(so, req, m, nam, control);
        rp = sotorawcb(so);
        if (req == PRU_ATTACH && rp) {
                int af = rp->rcb_proto.sp_protocol;
                if (error) {
                        free((caddr_t)rp, M_PCB);
+                       splx(s);
                        return (error);
                }
                if (af == AF_INET)
                        return (error);
                }
                if (af == AF_INET)
@@ -81,9 +74,12 @@ route_usrreq(so, req, m, nam, control)
                soisconnected(so);
                so->so_options |= SO_USELOOPBACK;
        }
                soisconnected(so);
                so->so_options |= SO_USELOOPBACK;
        }
+       splx(s);
        return (error);
 }
        return (error);
 }
-#define ROUNDUP(a) (1 + (((a) - 1) | (sizeof(long) - 1)))
+#define ROUNDUP(a) \
+       ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
 
 /*ARGSUSED*/
 route_output(m, so)
 
 /*ARGSUSED*/
 route_output(m, so)
@@ -94,8 +90,12 @@ route_output(m, so)
        register struct rtentry *rt = 0;
        struct rtentry *saved_nrt = 0;
        struct sockaddr *dst = 0, *gate = 0, *netmask = 0, *genmask = 0;
        register struct rtentry *rt = 0;
        struct rtentry *saved_nrt = 0;
        struct sockaddr *dst = 0, *gate = 0, *netmask = 0, *genmask = 0;
+       struct sockaddr *ifpaddr = 0, *ifaaddr;
        caddr_t cp, lim;
        int len, error = 0;
        caddr_t cp, lim;
        int len, error = 0;
+       struct ifnet *ifp = 0;
+       struct  ifaddr *ifa;
+       extern struct ifaddr *ifaof_ifpforaddr(), *ifa_ifwithroute();
 
 #define senderr(e) { error = e; goto flush;}
        if (m == 0 || m->m_len < sizeof(long))
 
 #define senderr(e) { error = e; goto flush;}
        if (m == 0 || m->m_len < sizeof(long))
@@ -105,8 +105,8 @@ route_output(m, so)
        if ((m->m_flags & M_PKTHDR) == 0)
                panic("route_output");
        len = m->m_pkthdr.len;
        if ((m->m_flags & M_PKTHDR) == 0)
                panic("route_output");
        len = m->m_pkthdr.len;
-       rtm = mtod(m, struct rt_msghdr *);
-       if (len < rtm->rtm_msglen)
+       if (len < sizeof(*rtm) ||
+           len != mtod(m, struct rt_msghdr *)->rtm_msglen)
                senderr(EINVAL);
        R_Malloc(rtm, struct rt_msghdr *, len);
        if (rtm == 0)
                senderr(EINVAL);
        R_Malloc(rtm, struct rt_msghdr *, len);
        if (rtm == 0)
@@ -119,23 +119,29 @@ route_output(m, so)
        cp = (caddr_t) (rtm + 1);
        if (rtm->rtm_addrs & RTA_DST) {
                dst = (struct sockaddr *)cp;
        cp = (caddr_t) (rtm + 1);
        if (rtm->rtm_addrs & RTA_DST) {
                dst = (struct sockaddr *)cp;
-               cp += ROUNDUP(dst->sa_len);
+               ADVANCE(cp, dst);
        } else
                senderr(EINVAL);
        if ((rtm->rtm_addrs & RTA_GATEWAY) && cp < lim)  {
                gate = (struct sockaddr *)cp;
        } else
                senderr(EINVAL);
        if ((rtm->rtm_addrs & RTA_GATEWAY) && cp < lim)  {
                gate = (struct sockaddr *)cp;
-               cp += ROUNDUP(gate->sa_len);
+               ADVANCE(cp, gate);
        }
        if ((rtm->rtm_addrs & RTA_NETMASK) && cp < lim)  {
                netmask = (struct sockaddr *)cp;
        }
        if ((rtm->rtm_addrs & RTA_NETMASK) && cp < lim)  {
                netmask = (struct sockaddr *)cp;
-               if (*cp)
-                       cp += ROUNDUP(netmask->sa_len);
-               else
-                       cp += sizeof(long);
-
+               ADVANCE(cp, netmask);
        }
        if ((rtm->rtm_addrs & RTA_GENMASK) && cp < lim)  {
        }
        if ((rtm->rtm_addrs & RTA_GENMASK) && cp < lim)  {
+               struct radix_node *t, *rn_addmask();
                genmask = (struct sockaddr *)cp;
                genmask = (struct sockaddr *)cp;
+               ADVANCE(cp, genmask);
+               t = rn_addmask(genmask, 1, 2);
+               if (t && Bcmp(genmask, t->rn_key, *(u_char *)genmask) == 0)
+                       genmask = (struct sockaddr *)(t->rn_key);
+               else
+                       senderr(ENOBUFS);
+       }
+       if ((rtm->rtm_addrs & RTA_IFP) && cp < lim)  {
+               ifpaddr = (struct sockaddr *)cp;
        }
        switch (rtm->rtm_type) {
        case RTM_ADD:
        }
        switch (rtm->rtm_type) {
        case RTM_ADD:
@@ -147,6 +153,7 @@ route_output(m, so)
                        rt_setmetrics(rtm->rtm_inits,
                                &rtm->rtm_rmx, &saved_nrt->rt_rmx);
                        saved_nrt->rt_refcnt--;
                        rt_setmetrics(rtm->rtm_inits,
                                &rtm->rtm_rmx, &saved_nrt->rt_rmx);
                        saved_nrt->rt_refcnt--;
+                       saved_nrt->rt_genmask = genmask;
                }
                break;
 
                }
                break;
 
@@ -161,20 +168,47 @@ route_output(m, so)
                rt = rtalloc1(dst, 0);
                if (rt == 0)
                        senderr(ESRCH);
                rt = rtalloc1(dst, 0);
                if (rt == 0)
                        senderr(ESRCH);
+               if (((struct radix_node *)rt)->rn_dupedkey) {
+                       if (netmask == 0 && rtm->rtm_type != RTM_GET)
+                               senderr(ETOOMANYREFS);
+               }
                switch(rtm->rtm_type) {
                switch(rtm->rtm_type) {
-                        struct sockaddr *outmask;
 
                case RTM_GET:
 
                case RTM_GET:
-                       netmask = rt_mask(rt);
-                       len = sizeof(*rtm) + ROUNDUP(rt_key(rt)->sa_len);
-                       rtm->rtm_addrs = RTA_DST;
-                       if (rt->rt_gateway) {
-                               len += ROUNDUP(rt->rt_gateway->sa_len);
+                       dst = rt_key(rt); len = sizeof(*rtm);
+                       ADVANCE(len, dst);
+                       rtm->rtm_addrs |= RTA_DST;
+                       if (gate = rt->rt_gateway) {
+                               ADVANCE(len, gate);
                                rtm->rtm_addrs |= RTA_GATEWAY;
                                rtm->rtm_addrs |= RTA_GATEWAY;
-                       }
-                       if (netmask) {
-                               len += netmask->sa_len;
+                       } else
+                               rtm->rtm_addrs &= ~RTA_GATEWAY;
+                       if (netmask = rt_mask(rt)) {
+                               ADVANCE(len, netmask);
                                rtm->rtm_addrs |= RTA_NETMASK;
                                rtm->rtm_addrs |= RTA_NETMASK;
+                       } else
+                               rtm->rtm_addrs &= ~RTA_NETMASK;
+                       if (genmask = rt->rt_genmask) {
+                               ADVANCE(len, genmask);
+                               rtm->rtm_addrs |= RTA_GENMASK;
+                       } else
+                               rtm->rtm_addrs &= ~RTA_GENMASK;
+                       if (rtm->rtm_addrs & (RTA_IFP | RTA_IFA)) {
+                               if (rt->rt_ifp == 0)
+                                       goto badif;
+                               for (ifa = rt->rt_ifp->if_addrlist;
+                                   ifa && ifa->ifa_addr->sa_family != AF_LINK;
+                                    ifa = ifa->ifa_next){}
+                               if (ifa && rt->rt_ifa) {
+                                       ifpaddr = ifa->ifa_addr;
+                                       ADVANCE(len, ifpaddr);
+                                       ifaaddr = rt->rt_ifa->ifa_addr;
+                                       ADVANCE(len, ifaaddr);
+                                       rtm->rtm_addrs |= RTA_IFP | RTA_IFA;
+                               } else {
+                               badif:  ifpaddr = 0;
+                                       rtm->rtm_addrs &= ~(RTA_IFP | RTA_IFA);
+                               }
                        }
                        if (len > rtm->rtm_msglen) {
                                struct rt_msghdr *new_rtm;
                        }
                        if (len > rtm->rtm_msglen) {
                                struct rt_msghdr *new_rtm;
@@ -183,32 +217,67 @@ route_output(m, so)
                                        senderr(ENOBUFS);
                                Bcopy(rtm, new_rtm, rtm->rtm_msglen);
                                Free(rtm); rtm = new_rtm;
                                        senderr(ENOBUFS);
                                Bcopy(rtm, new_rtm, rtm->rtm_msglen);
                                Free(rtm); rtm = new_rtm;
-                               gate = (struct sockaddr *)
-                                   (ROUNDUP(rt->rt_gateway->sa_len)
-                                                               + (char *)dst);
-                               Bcopy(&rt->rt_gateway, gate,
-                                               rt->rt_gateway->sa_len);
-                               rtm->rtm_flags = rt->rt_flags;
-                               if (netmask) {
-                                   outmask = (struct sockaddr *)
-                                      (ROUNDUP(netmask->sa_len)+(char *)gate);
-                                   Bcopy(netmask, outmask, netmask->sa_len);
-                               }
+                       }
+                       rtm->rtm_msglen = len;
+                       rtm->rtm_flags = rt->rt_flags;
+                       rtm->rtm_rmx = rt->rt_rmx;
+                       cp = (caddr_t) (1 + rtm);
+                       len = ROUNDUP(dst->sa_len); 
+                       Bcopy(dst, cp, len); cp += len;
+                       if (gate) {
+                           len = ROUNDUP(gate->sa_len);
+                           Bcopy(gate, cp, len); cp += len;
+                       }
+                       if (netmask) {
+                           len = ROUNDUP(netmask->sa_len);
+                           Bcopy(netmask, cp, len); cp += len;
+                       }
+                       if (genmask) {
+                           len = ROUNDUP(genmask->sa_len);
+                           Bcopy(genmask, cp, len); cp += len;
+                       }
+                       if (ifpaddr) {
+                           len = ROUNDUP(ifpaddr->sa_len);
+                           Bcopy(ifpaddr, cp, len); cp += len;
+                           len = ROUNDUP(ifaaddr->sa_len);
+                           Bcopy(ifaaddr, cp, len); cp += len;
                        }
                        break;
 
                case RTM_CHANGE:
                        }
                        break;
 
                case RTM_CHANGE:
-                       if (gate == 0)
-                               senderr(EINVAL);
-                       if (gate->sa_len > (len = rt->rt_gateway->sa_len))
+                       if (gate &&
+                           (gate->sa_len > (len = rt->rt_gateway->sa_len)))
                                senderr(EDQUOT);
                        if (rt->rt_ifa && rt->rt_ifa->ifa_rtrequest)
                                senderr(EDQUOT);
                        if (rt->rt_ifa && rt->rt_ifa->ifa_rtrequest)
-                               rt->rt_ifa->ifa_rtrequest(RTM_CHANGE, rt, gate);
-                       Bcopy(gate, rt->rt_gateway, len);
-                       rt->rt_gateway->sa_len = len;
-               
+                               rt->rt_ifa->ifa_rtrequest(RTM_DELETE, rt, gate);
+                       /* new gateway could require new ifaddr, ifp;
+                          flags may also be different; ifp may be specified
+                          by ll sockaddr when protocol address is ambiguous */
+                       if (ifpaddr &&
+                           (ifa = ifa_ifwithnet(ifpaddr)) &&
+                           (ifp = ifa->ifa_ifp) &&
+                           (ifa = ifaof_ifpforaddr(gate, ifp))) {
+                                    /* We got it */
+                       } else {
+                           ifa = 0; ifp = 0;
+                       }
+                       if (gate)
+                               Bcopy(gate, rt->rt_gateway, len);
                        rt_setmetrics(rtm->rtm_inits,
                                &rtm->rtm_rmx, &rt->rt_rmx);
                        rt_setmetrics(rtm->rtm_inits,
                                &rtm->rtm_rmx, &rt->rt_rmx);
+                       if (ifa == 0)
+                           ifa = ifa_ifwithroute(rt->rt_flags, rt_key(rt),
+                                               gate);
+                       if (ifa) {
+                               if (rt->rt_ifa != ifa) {
+                                   rt->rt_ifa = ifa;
+                                   rt->rt_ifp = ifa->ifa_ifp;
+                               }
+                       }
+                       if (rt->rt_ifa && rt->rt_ifa->ifa_rtrequest)
+                              rt->rt_ifa->ifa_rtrequest(RTM_ADD, rt, gate);
+                       if (genmask)
+                               rt->rt_genmask = genmask;
                        /*
                         * Fall into
                         */
                        /*
                         * Fall into
                         */
@@ -249,13 +318,14 @@ cleanup:
                /* There is another listener, so construct message */
                rp = sotorawcb(so);
        }
                /* There is another listener, so construct message */
                rp = sotorawcb(so);
        }
-       if (cp = (caddr_t)rtm) {
-               m_copyback(m, 0, len, cp);
+       if (rtm) {
+               m_copyback(m, 0, rtm->rtm_msglen, (caddr_t)rtm);
                Free(rtm);
        }
        if (rp)
                rp->rcb_proto.sp_family = 0; /* Avoid us */
                Free(rtm);
        }
        if (rp)
                rp->rcb_proto.sp_family = 0; /* Avoid us */
-       route_proto.sp_protocol = dst->sa_family;
+       if (dst)
+               route_proto.sp_protocol = dst->sa_family;
        raw_input(m, &route_proto, &route_src, &route_dst);
        if (rp)
                rp->rcb_proto.sp_family = PF_ROUTE;
        raw_input(m, &route_proto, &route_src, &route_dst);
        if (rp)
                rp->rcb_proto.sp_family = PF_ROUTE;
@@ -263,7 +333,7 @@ cleanup:
        return (error);
 }
 
        return (error);
 }
 
-static rt_setmetrics(which, in, out)
+rt_setmetrics(which, in, out)
        u_long which;
        register struct rt_metrics *in, *out;
 {
        u_long which;
        register struct rt_metrics *in, *out;
 {
@@ -369,10 +439,7 @@ struct sockaddr *gate, *mask, *src;
                rtm->rtm_addrs |= RTA_GATEWAY;
        }
        if (mask) {
                rtm->rtm_addrs |= RTA_GATEWAY;
        }
        if (mask) {
-               if (mask->sa_len)
-                       dlen = ROUNDUP(mask->sa_len);
-               else
-                       dlen = sizeof(long);
+               dlen = ROUNDUP(mask->sa_len);
                m_copyback(m, len ,  dlen, (caddr_t)mask);
                len += dlen;
                rtm->rtm_addrs |= RTA_NETMASK;
                m_copyback(m, len ,  dlen, (caddr_t)mask);
                len += dlen;
                rtm->rtm_addrs |= RTA_NETMASK;
@@ -429,8 +496,7 @@ rt_dumpentry(rn, w)
        if (sa = rt->rt_gateway)
                next(RTA_GATEWAY, ROUNDUP(sa->sa_len));
        if (sa = rt_mask(rt))
        if (sa = rt->rt_gateway)
                next(RTA_GATEWAY, ROUNDUP(sa->sa_len));
        if (sa = rt_mask(rt))
-               next(RTA_NETMASK,
-                       sa->sa_len ? ROUNDUP(sa->sa_len) : sizeof(long));
+               next(RTA_NETMASK, ROUNDUP(sa->sa_len));
        if (sa = rt->rt_genmask)
                next(RTA_GENMASK, ROUNDUP(sa->sa_len));
        w->w_needed += size;
        if (sa = rt->rt_genmask)
                next(RTA_GENMASK, ROUNDUP(sa->sa_len));
        w->w_needed += size;
@@ -450,7 +516,7 @@ rt_dumpentry(rn, w)
                if (sa = rt->rt_gateway)
                        next(ROUNDUP(sa->sa_len));
                if (sa = rt_mask(rt))
                if (sa = rt->rt_gateway)
                        next(ROUNDUP(sa->sa_len));
                if (sa = rt_mask(rt))
-                       next(sa->sa_len ? ROUNDUP(sa->sa_len) : sizeof(long));
+                       next(ROUNDUP(sa->sa_len));
                if (sa = rt->rt_genmask)
                        next(ROUNDUP(sa->sa_len));
 #undef next
                if (sa = rt->rt_genmask)
                        next(ROUNDUP(sa->sa_len));
 #undef next
@@ -467,7 +533,7 @@ rt_dumpentry(rn, w)
        if (sa = rt->rt_gateway)
                next(sa, ROUNDUP(sa->sa_len));
        if (sa = rt_mask(rt))
        if (sa = rt->rt_gateway)
                next(sa, ROUNDUP(sa->sa_len));
        if (sa = rt_mask(rt))
-               next(sa, sa->sa_len ? ROUNDUP(sa->sa_len) : sizeof(long));
+               next(sa, ROUNDUP(sa->sa_len));
        if (sa = rt->rt_genmask)
                next(sa, ROUNDUP(sa->sa_len));
     }
        if (sa = rt->rt_genmask)
                next(sa, ROUNDUP(sa->sa_len));
     }
@@ -550,11 +616,6 @@ struct protosw routesw[] = {
   raw_input,   route_output,   raw_ctlinput,   0,
   route_usrreq,
   raw_init,    0,              0,              0,
   raw_input,   route_output,   raw_ctlinput,   0,
   route_usrreq,
   raw_init,    0,              0,              0,
-},
-{ 0,           0,              0,              0,
-  raw_input,   0,              raw_ctlinput,   0,
-  raw_usrreq,
-  raw_init,    0,              0,              0,
 }
 };
 
 }
 };