tech-net archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

ip_pcbopts() to sockopt



Hi,

proceeding with pushing the socket options code further down the stack,
I've been looking at ip_pcbopts() which handles creating a 'IP options'
mbuf from the user provided data. My patch is attached but its not so
clear, so I've included the actual updated function below.

I think its right, and traceroute (which uses IP_OPTIONS to set a single
source routing address) still works for what thats worth.

points to note

- _EOL and _NOP options are not required to be in the options list
  according to RFC791 and could be ignored but we did include them before
  so I left them in. (presumably if the user didn't want them in, she
  would leave them out :)

- previously if there was an _EOL then any data after the end of that
  would get left in the mbuf to be attached to outgoing packets I think.
  Not sure if that was intended?  I've stopped copying at _EOL ..

- no checking is done of [IPOPT_OFFSET] which contains the offset of
  the 'next' address. the RFC says it has a minimum legal value
  of 4 (IPOPT_MINOFF) but perhaps that doesn't matter?

is this ok to commit?

(I can't claim to have a great understanding of this code :)

iain

/*
 * Set up IP options in pcb for insertion in output packets.
 * Store in mbuf with pointer in pcbopt, adding pseudo-option
 * with destination address if source routed.
 */
int
ip_pcbopts(struct mbuf **pcbopt, const struct sockopt *sopt)
{
        struct mbuf *m;
        const u_char *cp;
        u_char *dp;
        int cnt;
        uint8_t optval, olen, offset;

        /* turn off any old options */
        if (*pcbopt)
                (void)m_free(*pcbopt);
        *pcbopt = NULL;

        cp = sopt->sopt_data;
        cnt = sopt->sopt_size;

        if (cnt == 0)
                return (0);     /* Only turning off any previous options */

#ifndef __vax__
        if (cnt % sizeof(int32_t))
                return (EINVAL);
#endif

        m = m_get(M_WAIT, MT_SOOPTS);
        dp = mtod(m, u_char *);
        memset(dp, 0, sizeof(struct in_addr));
        dp += sizeof(struct in_addr);
        m->m_len = sizeof(struct in_addr);

        /*
         * IP option list according to RFC791. Each option is of the form
         *
         *      [optval] [olen] [(olen - 2) data bytes]
         *
         * we validate the list and copy options to an mbuf for prepending
         * to data packets. The IP first-hop destination address will be
         * stored before actual options and is zero if unset.
         */
        while (cnt > 0) {
                optval = cp[IPOPT_OPTVAL];

                if (optval == IPOPT_EOL || optval == IPOPT_NOP) {
                        olen = 1;
                } else {
                        if (cnt < IPOPT_OLEN + 1)
                                goto bad;

                        olen = cp[IPOPT_OLEN];
                        if (olen < IPOPT_OLEN + 1 || olen > cnt)
                                goto bad;
                }

                if (optval == IPOPT_LSRR || optval == IPOPT_SSRR) {
                        /*
                         * user process specifies route as:
                         *      ->A->B->C->D
                         * D must be our final destination (but we can't
                         * check that since we may not have connected yet).
                         * A is first hop destination, which doesn't appear in
                         * actual IP option, but is stored before the options.
                         */
                        if (olen < IPOPT_OFFSET + 1 + sizeof(struct in_addr))
                                goto bad;

                        offset = cp[IPOPT_OFFSET];
                        memcpy(mtod(m, u_char *), cp + IPOPT_OFFSET + 1,
                            sizeof(struct in_addr));

                        cp += sizeof(struct in_addr);
                        cnt -= sizeof(struct in_addr);
                        olen -= sizeof(struct in_addr);

                        if (m->m_len + olen > MAX_IPOPTLEN + sizeof(struct 
in_addr))
                                goto bad;

                        memcpy(dp, cp, olen);
                        dp[IPOPT_OPTVAL] = optval;
                        dp[IPOPT_OLEN] = olen;
                        dp[IPOPT_OFFSET] = offset;
                        break;
                } else {
                        if (m->m_len + olen > MAX_IPOPTLEN + sizeof(struct 
in_addr))
                                goto bad;

                        memcpy(dp, cp, olen);
                        break;
                }

                dp += olen;
                m->m_len += olen;

                if (optval == IPOPT_EOL)
                        break;

                cp += olen;
                cnt -= olen;
        }

        *pcbopt = m;
        return (0);

bad:
        (void)m_free(m);
        return (EINVAL);
}
--- /usr/src/sys/netinet/ip_output.c    2008-08-16 22:51:43.000000000 +0100
+++ ip_output.c 2008-08-16 22:53:07.000000000 +0100
@@ -1216,18 +1216,8 @@ ip_ctloutput(int op, struct socket *so, 
 #ifdef notyet
                case IP_RETOPTS:
 #endif
-                   {
-                       struct mbuf *m;
-
-                       m = sockopt_getmbuf(sopt);
-                       if (m == NULL) {
-                               error = ENOBUFS;
-                               break;
-                       }
-
-                       error = ip_pcbopts(&inp->inp_options, m);
+                       error = ip_pcbopts(&inp->inp_options, sopt);
                        break;
-                   }
 
                case IP_TOS:
                case IP_TTL:
@@ -1430,62 +1420,60 @@ ip_ctloutput(int op, struct socket *so, 
  * with destination address if source routed.
  */
 int
-ip_pcbopts(struct mbuf **pcbopt, struct mbuf *m)
+ip_pcbopts(struct mbuf **pcbopt, const struct sockopt *sopt)
 {
-       int cnt, optlen;
-       u_char *cp;
-       u_char opt;
+       struct mbuf *m;
+       const u_char *cp;
+       u_char *dp;
+       int cnt;
+       uint8_t optval, olen, offset;
 
        /* turn off any old options */
        if (*pcbopt)
                (void)m_free(*pcbopt);
-       *pcbopt = 0;
-       if (m == (struct mbuf *)0 || m->m_len == 0) {
-               /*
-                * Only turning off any previous options.
-                */
-               if (m)
-                       (void)m_free(m);
-               return (0);
-       }
+       *pcbopt = NULL;
+
+       cp = sopt->sopt_data;
+       cnt = sopt->sopt_size;
+
+       if (cnt == 0)
+               return (0);     /* Only turning off any previous options */
 
 #ifndef        __vax__
-       if (m->m_len % sizeof(int32_t))
-               goto bad;
+       if (cnt % sizeof(int32_t))
+               return (EINVAL);
 #endif
+
+       m = m_get(M_WAIT, MT_SOOPTS);
+       dp = mtod(m, u_char *);
+       memset(dp, 0, sizeof(struct in_addr));
+       dp += sizeof(struct in_addr);
+       m->m_len = sizeof(struct in_addr);
+
        /*
-        * IP first-hop destination address will be stored before
-        * actual options; move other options back
-        * and clear it when none present.
+        * IP option list according to RFC791. Each option is of the form
+        *
+        *      [optval] [olen] [(olen - 2) data bytes]
+        *
+        * we validate the list and copy options to an mbuf for prepending
+        * to data packets. The IP first-hop destination address will be
+        * stored before actual options and is zero if unset.
         */
-       if (m->m_data + m->m_len + sizeof(struct in_addr) >= &m->m_dat[MLEN])
-               goto bad;
-       cnt = m->m_len;
-       m->m_len += sizeof(struct in_addr);
-       cp = mtod(m, u_char *) + sizeof(struct in_addr);
-       memmove(cp, mtod(m, void *), (unsigned)cnt);
-       bzero(mtod(m, void *), sizeof(struct in_addr));
+       while (cnt > 0) {
+               optval = cp[IPOPT_OPTVAL];
 
-       for (; cnt > 0; cnt -= optlen, cp += optlen) {
-               opt = cp[IPOPT_OPTVAL];
-               if (opt == IPOPT_EOL)
-                       break;
-               if (opt == IPOPT_NOP)
-                       optlen = 1;
-               else {
-                       if (cnt < IPOPT_OLEN + sizeof(*cp))
+               if (optval == IPOPT_EOL || optval == IPOPT_NOP) {
+                       olen = 1;
+               } else {
+                       if (cnt < IPOPT_OLEN + 1)
                                goto bad;
-                       optlen = cp[IPOPT_OLEN];
-                       if (optlen < IPOPT_OLEN  + sizeof(*cp) || optlen > cnt)
+
+                       olen = cp[IPOPT_OLEN];
+                       if (olen < IPOPT_OLEN + 1 || olen > cnt)
                                goto bad;
                }
-               switch (opt) {
-
-               default:
-                       break;
 
-               case IPOPT_LSRR:
-               case IPOPT_SSRR:
+               if (optval == IPOPT_LSRR || optval == IPOPT_SSRR) {
                        /*
                         * user process specifies route as:
                         *      ->A->B->C->D
@@ -1494,29 +1482,43 @@ ip_pcbopts(struct mbuf **pcbopt, struct 
                         * A is first hop destination, which doesn't appear in
                         * actual IP option, but is stored before the options.
                         */
-                       if (optlen < IPOPT_MINOFF - 1 + sizeof(struct in_addr))
+                       if (olen < IPOPT_OFFSET + 1 + sizeof(struct in_addr))
                                goto bad;
-                       m->m_len -= sizeof(struct in_addr);
-                       cnt -= sizeof(struct in_addr);
-                       optlen -= sizeof(struct in_addr);
-                       cp[IPOPT_OLEN] = optlen;
-                       /*
-                        * Move first hop before start of options.
-                        */
-                       bcopy((void *)&cp[IPOPT_OFFSET+1], mtod(m, void *),
+
+                       offset = cp[IPOPT_OFFSET];
+                       memcpy(mtod(m, u_char *), cp + IPOPT_OFFSET + 1,
                            sizeof(struct in_addr));
-                       /*
-                        * Then copy rest of options back
-                        * to close up the deleted entry.
-                        */
-                       (void)memmove(&cp[IPOPT_OFFSET+1],
-                           &cp[IPOPT_OFFSET+1] + sizeof(struct in_addr),
-                           (unsigned)cnt - (IPOPT_MINOFF - 1));
+
+                       cp += sizeof(struct in_addr);
+                       cnt -= sizeof(struct in_addr);
+                       olen -= sizeof(struct in_addr);
+
+                       if (m->m_len + olen > MAX_IPOPTLEN + sizeof(struct 
in_addr))
+                               goto bad;
+
+                       memcpy(dp, cp, olen);
+                       dp[IPOPT_OPTVAL] = optval;
+                       dp[IPOPT_OLEN] = olen;
+                       dp[IPOPT_OFFSET] = offset;
+                       break;
+               } else {
+                       if (m->m_len + olen > MAX_IPOPTLEN + sizeof(struct 
in_addr))
+                               goto bad;
+
+                       memcpy(dp, cp, olen);
                        break;
                }
+
+               dp += olen;
+               m->m_len += olen;
+
+               if (optval == IPOPT_EOL)
+                       break;
+
+               cp += olen;
+               cnt -= olen;
        }
-       if (m->m_len > MAX_IPOPTLEN + sizeof(struct in_addr))
-               goto bad;
+
        *pcbopt = m;
        return (0);
 
--- /usr/src/sys/netinet/ip_var.h       2008-08-16 22:51:44.000000000 +0100
+++ ip_var.h    2008-08-16 22:53:12.000000000 +0100
@@ -214,7 +214,7 @@ int  ip_optcopy(struct ip *, struct ip *
 u_int   ip_optlen(struct inpcb *);
 int     ip_output(struct mbuf *, ...);
 int     ip_fragment(struct mbuf *, struct ifnet *, u_long);
-int     ip_pcbopts(struct mbuf **, struct mbuf *);
+int     ip_pcbopts(struct mbuf **, const struct sockopt *);
 struct mbuf *
         ip_reass(struct ipqent *, struct ipq *, struct ipqhead *);
 struct in_ifaddr *


Home | Main Index | Thread Index | Old Index