[prev in list] [next in list] [prev in thread] [next in thread] 

List:       netbsd-tech-net
Subject:    ip_pcbopts() to sockopt
From:       Iain Hibbert <plunky () rya-online ! net>
Date:       2008-08-16 22:15:26
Message-ID: 1218924926.757129.1819.nullmailer () galant ! ukfsn ! org
[Download RAW message or body]

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);
}
["diff" (TEXT/PLAIN)]

--- /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 *


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic