ip_options.c revision 194760
196263Sobrien/*
296263Sobrien * Copyright (c) 1982, 1986, 1988, 1993
396263Sobrien *      The Regents of the University of California.
496263Sobrien * Copyright (c) 2005 Andre Oppermann, Internet Business Solutions AG.
596263Sobrien * All rights reserved.
696263Sobrien *
796263Sobrien * Redistribution and use in source and binary forms, with or without
896263Sobrien * modification, are permitted provided that the following conditions
996263Sobrien * are met:
1096263Sobrien * 1. Redistributions of source code must retain the above copyright
1196263Sobrien *    notice, this list of conditions and the following disclaimer.
1296263Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1396263Sobrien *    notice, this list of conditions and the following disclaimer in the
1496263Sobrien *    documentation and/or other materials provided with the distribution.
1596263Sobrien * 4. Neither the name of the University nor the names of its contributors
1696263Sobrien *    may be used to endorse or promote products derived from this software
1796263Sobrien *    without specific prior written permission.
1896263Sobrien *
1996263Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2096263Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2196263Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2296263Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2396263Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2496263Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2596263Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2696263Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2796263Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2896263Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2996263Sobrien * SUCH DAMAGE.
3096263Sobrien */
3196263Sobrien
3296263Sobrien#include <sys/cdefs.h>
3396263Sobrien__FBSDID("$FreeBSD: head/sys/netinet/ip_options.c 194760 2009-06-23 20:19:09Z rwatson $");
3496263Sobrien
3596263Sobrien#include "opt_ipstealth.h"
3696263Sobrien
3796263Sobrien#include <sys/param.h>
3896263Sobrien#include <sys/systm.h>
3996263Sobrien#include <sys/mbuf.h>
4096263Sobrien#include <sys/domain.h>
4196263Sobrien#include <sys/protosw.h>
4296263Sobrien#include <sys/socket.h>
4396263Sobrien#include <sys/time.h>
4496263Sobrien#include <sys/kernel.h>
4596263Sobrien#include <sys/syslog.h>
4696263Sobrien#include <sys/sysctl.h>
4796263Sobrien#include <sys/vimage.h>
4896263Sobrien
4996263Sobrien#include <net/if.h>
5096263Sobrien#include <net/if_types.h>
5196263Sobrien#include <net/if_var.h>
5296263Sobrien#include <net/if_dl.h>
5396263Sobrien#include <net/route.h>
5496263Sobrien#include <net/netisr.h>
5596263Sobrien
5696263Sobrien#include <netinet/in.h>
5796263Sobrien#include <netinet/in_systm.h>
5896263Sobrien#include <netinet/in_var.h>
5996263Sobrien#include <netinet/ip.h>
6096263Sobrien#include <netinet/in_pcb.h>
6196263Sobrien#include <netinet/ip_var.h>
6296263Sobrien#include <netinet/ip_options.h>
6396263Sobrien#include <netinet/ip_icmp.h>
6496263Sobrien#include <machine/in_cksum.h>
6596263Sobrien#include <netinet/vinet.h>
6696263Sobrien
6796263Sobrien#include <sys/socketvar.h>
6896263Sobrien
6996263Sobrien#include <security/mac/mac_framework.h>
7096263Sobrien
7196263Sobrienstatic int	ip_dosourceroute = 0;
7296263SobrienSYSCTL_INT(_net_inet_ip, IPCTL_SOURCEROUTE, sourceroute, CTLFLAG_RW,
7396263Sobrien    &ip_dosourceroute, 0, "Enable forwarding source routed IP packets");
7496263Sobrien
7596263Sobrienstatic int	ip_acceptsourceroute = 0;
7696263SobrienSYSCTL_INT(_net_inet_ip, IPCTL_ACCEPTSOURCEROUTE, accept_sourceroute,
7796263Sobrien    CTLFLAG_RW, &ip_acceptsourceroute, 0,
7896263Sobrien    "Enable accepting source routed IP packets");
7996263Sobrien
8096263Sobrienint		ip_doopts = 1;	/* 0 = ignore, 1 = process, 2 = reject */
8196263SobrienSYSCTL_INT(_net_inet_ip, OID_AUTO, process_options, CTLFLAG_RW,
8296263Sobrien    &ip_doopts, 0, "Enable IP options processing ([LS]SRR, RR, TS)");
8396263Sobrien
8496263Sobrienstatic void	save_rte(struct mbuf *m, u_char *, struct in_addr);
8596263Sobrien
8696263Sobrien/*
8796263Sobrien * Do option processing on a datagram, possibly discarding it if bad options
8896263Sobrien * are encountered, or forwarding it if source-routed.
8996263Sobrien *
9096263Sobrien * The pass argument is used when operating in the IPSTEALTH mode to tell
9196263Sobrien * what options to process: [LS]SRR (pass 0) or the others (pass 1).  The
9296263Sobrien * reason for as many as two passes is that when doing IPSTEALTH, non-routing
9396263Sobrien * options should be processed only if the packet is for us.
9496263Sobrien *
9596263Sobrien * Returns 1 if packet has been forwarded/freed, 0 if the packet should be
9696263Sobrien * processed further.
9796263Sobrien */
9896263Sobrienint
9996263Sobrienip_dooptions(struct mbuf *m, int pass)
10096263Sobrien{
10196263Sobrien	INIT_VNET_INET(curvnet);
10296263Sobrien	struct ip *ip = mtod(m, struct ip *);
10396263Sobrien	u_char *cp;
10496263Sobrien	struct in_ifaddr *ia;
10596263Sobrien	int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0;
10696263Sobrien	struct in_addr *sin, dst;
10796263Sobrien	uint32_t ntime;
10896263Sobrien	struct	sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET };
10996263Sobrien
11096263Sobrien	/* Ignore or reject packets with IP options. */
11196263Sobrien	if (ip_doopts == 0)
11296263Sobrien		return 0;
11396263Sobrien	else if (ip_doopts == 2) {
11496263Sobrien		type = ICMP_UNREACH;
11596263Sobrien		code = ICMP_UNREACH_FILTER_PROHIB;
11696263Sobrien		goto bad;
11796263Sobrien	}
11896263Sobrien
11996263Sobrien	dst = ip->ip_dst;
12096263Sobrien	cp = (u_char *)(ip + 1);
12196263Sobrien	cnt = (ip->ip_hl << 2) - sizeof (struct ip);
12296263Sobrien	for (; cnt > 0; cnt -= optlen, cp += optlen) {
12396263Sobrien		opt = cp[IPOPT_OPTVAL];
12496263Sobrien		if (opt == IPOPT_EOL)
12596263Sobrien			break;
12696263Sobrien		if (opt == IPOPT_NOP)
12796263Sobrien			optlen = 1;
12896263Sobrien		else {
12996263Sobrien			if (cnt < IPOPT_OLEN + sizeof(*cp)) {
13096263Sobrien				code = &cp[IPOPT_OLEN] - (u_char *)ip;
13196263Sobrien				goto bad;
13296263Sobrien			}
13396263Sobrien			optlen = cp[IPOPT_OLEN];
13496263Sobrien			if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt) {
13596263Sobrien				code = &cp[IPOPT_OLEN] - (u_char *)ip;
13696263Sobrien				goto bad;
13796263Sobrien			}
13896263Sobrien		}
13996263Sobrien		switch (opt) {
14096263Sobrien
14196263Sobrien		default:
14296263Sobrien			break;
14396263Sobrien
14496263Sobrien		/*
14596263Sobrien		 * Source routing with record.  Find interface with current
14696263Sobrien		 * destination address.  If none on this machine then drop if
14796263Sobrien		 * strictly routed, or do nothing if loosely routed.  Record
14896263Sobrien		 * interface address and bring up next address component.  If
14996263Sobrien		 * strictly routed make sure next address is on directly
15096263Sobrien		 * accessible net.
15196263Sobrien		 */
15296263Sobrien		case IPOPT_LSRR:
15396263Sobrien		case IPOPT_SSRR:
15496263Sobrien#ifdef IPSTEALTH
15596263Sobrien			if (V_ipstealth && pass > 0)
15696263Sobrien				break;
15796263Sobrien#endif
15896263Sobrien			if (optlen < IPOPT_OFFSET + sizeof(*cp)) {
15996263Sobrien				code = &cp[IPOPT_OLEN] - (u_char *)ip;
16096263Sobrien				goto bad;
16196263Sobrien			}
16296263Sobrien			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
16396263Sobrien				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
16496263Sobrien				goto bad;
16596263Sobrien			}
16696263Sobrien			ipaddr.sin_addr = ip->ip_dst;
16796263Sobrien			if (ifa_ifwithaddr_check((struct sockaddr *)&ipaddr)
16896263Sobrien			    == 0) {
16996263Sobrien				if (opt == IPOPT_SSRR) {
17096263Sobrien					type = ICMP_UNREACH;
17196263Sobrien					code = ICMP_UNREACH_SRCFAIL;
17296263Sobrien					goto bad;
17396263Sobrien				}
17496263Sobrien				if (!ip_dosourceroute)
17596263Sobrien					goto nosourcerouting;
17696263Sobrien				/*
17796263Sobrien				 * Loose routing, and not at next destination
17896263Sobrien				 * yet; nothing to do except forward.
17996263Sobrien				 */
18096263Sobrien				break;
18196263Sobrien			}
18296263Sobrien			off--;			/* 0 origin */
18396263Sobrien			if (off > optlen - (int)sizeof(struct in_addr)) {
18496263Sobrien				/*
18596263Sobrien				 * End of source route.  Should be for us.
18696263Sobrien				 */
18796263Sobrien				if (!ip_acceptsourceroute)
18896263Sobrien					goto nosourcerouting;
18996263Sobrien				save_rte(m, cp, ip->ip_src);
19096263Sobrien				break;
19196263Sobrien			}
19296263Sobrien#ifdef IPSTEALTH
19396263Sobrien			if (V_ipstealth)
19496263Sobrien				goto dropit;
19596263Sobrien#endif
19696263Sobrien			if (!ip_dosourceroute) {
19796263Sobrien				if (V_ipforwarding) {
19896263Sobrien					char buf[16]; /* aaa.bbb.ccc.ddd\0 */
19996263Sobrien					/*
20096263Sobrien					 * Acting as a router, so generate
20196263Sobrien					 * ICMP
20296263Sobrien					 */
20396263Sobriennosourcerouting:
20496263Sobrien					strcpy(buf, inet_ntoa(ip->ip_dst));
20596263Sobrien					log(LOG_WARNING,
20696263Sobrien					    "attempted source route from %s to %s\n",
20796263Sobrien					    inet_ntoa(ip->ip_src), buf);
20896263Sobrien					type = ICMP_UNREACH;
20996263Sobrien					code = ICMP_UNREACH_SRCFAIL;
21096263Sobrien					goto bad;
21196263Sobrien				} else {
21296263Sobrien					/*
21396263Sobrien					 * Not acting as a router, so
21496263Sobrien					 * silently drop.
21596263Sobrien					 */
21696263Sobrien#ifdef IPSTEALTH
21796263Sobriendropit:
21896263Sobrien#endif
21996263Sobrien					IPSTAT_INC(ips_cantforward);
22096263Sobrien					m_freem(m);
22196263Sobrien					return (1);
22296263Sobrien				}
22396263Sobrien			}
22496263Sobrien
22596263Sobrien			/*
22696263Sobrien			 * locate outgoing interface
22796263Sobrien			 */
22896263Sobrien			(void)memcpy(&ipaddr.sin_addr, cp + off,
22996263Sobrien			    sizeof(ipaddr.sin_addr));
23096263Sobrien
23196263Sobrien			if (opt == IPOPT_SSRR) {
23296263Sobrien#define	INA	struct in_ifaddr *
23396263Sobrien#define	SA	struct sockaddr *
23496263Sobrien			    if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == NULL)
23596263Sobrien				ia = (INA)ifa_ifwithnet((SA)&ipaddr);
23696263Sobrien			} else
23796263Sobrien/* XXX MRT 0 for routing */
23896263Sobrien				ia = ip_rtaddr(ipaddr.sin_addr, M_GETFIB(m));
23996263Sobrien			if (ia == NULL) {
24096263Sobrien				type = ICMP_UNREACH;
24196263Sobrien				code = ICMP_UNREACH_SRCFAIL;
24296263Sobrien				goto bad;
24396263Sobrien			}
24496263Sobrien			ip->ip_dst = ipaddr.sin_addr;
24596263Sobrien			(void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr),
24696263Sobrien			    sizeof(struct in_addr));
24796263Sobrien			ifa_free(&ia->ia_ifa);
24896263Sobrien			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
24996263Sobrien			/*
25096263Sobrien			 * Let ip_intr's mcast routing check handle mcast pkts
25196263Sobrien			 */
25296263Sobrien			forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr));
25396263Sobrien			break;
25496263Sobrien
25596263Sobrien		case IPOPT_RR:
25696263Sobrien#ifdef IPSTEALTH
25796263Sobrien			if (V_ipstealth && pass == 0)
25896263Sobrien				break;
25996263Sobrien#endif
26096263Sobrien			if (optlen < IPOPT_OFFSET + sizeof(*cp)) {
26196263Sobrien				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
26296263Sobrien				goto bad;
26396263Sobrien			}
26496263Sobrien			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
26596263Sobrien				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
26696263Sobrien				goto bad;
26796263Sobrien			}
26896263Sobrien			/*
26996263Sobrien			 * If no space remains, ignore.
27096263Sobrien			 */
27196263Sobrien			off--;			/* 0 origin */
27296263Sobrien			if (off > optlen - (int)sizeof(struct in_addr))
27396263Sobrien				break;
27496263Sobrien			(void)memcpy(&ipaddr.sin_addr, &ip->ip_dst,
27596263Sobrien			    sizeof(ipaddr.sin_addr));
27696263Sobrien			/*
27796263Sobrien			 * Locate outgoing interface; if we're the
27896263Sobrien			 * destination, use the incoming interface (should be
27996263Sobrien			 * same).
28096263Sobrien			 */
28196263Sobrien			if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == NULL &&
28296263Sobrien			    (ia = ip_rtaddr(ipaddr.sin_addr, M_GETFIB(m))) == NULL) {
28396263Sobrien				type = ICMP_UNREACH;
28496263Sobrien				code = ICMP_UNREACH_HOST;
28596263Sobrien				goto bad;
28696263Sobrien			}
28796263Sobrien			(void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr),
28896263Sobrien			    sizeof(struct in_addr));
28996263Sobrien			ifa_free(&ia->ia_ifa);
29096263Sobrien			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
29196263Sobrien			break;
29296263Sobrien
29396263Sobrien		case IPOPT_TS:
29496263Sobrien#ifdef IPSTEALTH
29596263Sobrien			if (V_ipstealth && pass == 0)
29696263Sobrien				break;
29796263Sobrien#endif
29896263Sobrien			code = cp - (u_char *)ip;
29996263Sobrien			if (optlen < 4 || optlen > 40) {
30096263Sobrien				code = &cp[IPOPT_OLEN] - (u_char *)ip;
30196263Sobrien				goto bad;
30296263Sobrien			}
30396263Sobrien			if ((off = cp[IPOPT_OFFSET]) < 5) {
30496263Sobrien				code = &cp[IPOPT_OLEN] - (u_char *)ip;
30596263Sobrien				goto bad;
30696263Sobrien			}
30796263Sobrien			if (off > optlen - (int)sizeof(int32_t)) {
30896263Sobrien				cp[IPOPT_OFFSET + 1] += (1 << 4);
30996263Sobrien				if ((cp[IPOPT_OFFSET + 1] & 0xf0) == 0) {
31096263Sobrien					code = &cp[IPOPT_OFFSET] - (u_char *)ip;
31196263Sobrien					goto bad;
31296263Sobrien				}
31396263Sobrien				break;
31496263Sobrien			}
31596263Sobrien			off--;				/* 0 origin */
31696263Sobrien			sin = (struct in_addr *)(cp + off);
31796263Sobrien			switch (cp[IPOPT_OFFSET + 1] & 0x0f) {
31896263Sobrien
31996263Sobrien			case IPOPT_TS_TSONLY:
32096263Sobrien				break;
32196263Sobrien
32296263Sobrien			case IPOPT_TS_TSANDADDR:
32396263Sobrien				if (off + sizeof(uint32_t) +
32496263Sobrien				    sizeof(struct in_addr) > optlen) {
32596263Sobrien					code = &cp[IPOPT_OFFSET] - (u_char *)ip;
32696263Sobrien					goto bad;
32796263Sobrien				}
32896263Sobrien				ipaddr.sin_addr = dst;
32996263Sobrien				ia = (INA)ifaof_ifpforaddr((SA)&ipaddr,
33096263Sobrien							    m->m_pkthdr.rcvif);
33196263Sobrien				if (ia == NULL)
33296263Sobrien					continue;
33396263Sobrien				(void)memcpy(sin, &IA_SIN(ia)->sin_addr,
33496263Sobrien				    sizeof(struct in_addr));
33596263Sobrien				ifa_free(&ia->ia_ifa);
33696263Sobrien				cp[IPOPT_OFFSET] += sizeof(struct in_addr);
33796263Sobrien				off += sizeof(struct in_addr);
33896263Sobrien				break;
33996263Sobrien
34096263Sobrien			case IPOPT_TS_PRESPEC:
34196263Sobrien				if (off + sizeof(uint32_t) +
34296263Sobrien				    sizeof(struct in_addr) > optlen) {
34396263Sobrien					code = &cp[IPOPT_OFFSET] - (u_char *)ip;
34496263Sobrien					goto bad;
34596263Sobrien				}
34696263Sobrien				(void)memcpy(&ipaddr.sin_addr, sin,
34796263Sobrien				    sizeof(struct in_addr));
34896263Sobrien				if (ifa_ifwithaddr((SA)&ipaddr) == NULL)
34996263Sobrien					continue;
35096263Sobrien				cp[IPOPT_OFFSET] += sizeof(struct in_addr);
35196263Sobrien				off += sizeof(struct in_addr);
35296263Sobrien				break;
35396263Sobrien
35496263Sobrien			default:
35596263Sobrien				code = &cp[IPOPT_OFFSET + 1] - (u_char *)ip;
35696263Sobrien				goto bad;
35796263Sobrien			}
35896263Sobrien			ntime = iptime();
35996263Sobrien			(void)memcpy(cp + off, &ntime, sizeof(uint32_t));
36096263Sobrien			cp[IPOPT_OFFSET] += sizeof(uint32_t);
36196263Sobrien		}
36296263Sobrien	}
36396263Sobrien	if (forward && V_ipforwarding) {
36496263Sobrien		ip_forward(m, 1);
36596263Sobrien		return (1);
36696263Sobrien	}
36796263Sobrien	return (0);
36896263Sobrienbad:
36996263Sobrien	icmp_error(m, type, code, 0, 0);
37096263Sobrien	IPSTAT_INC(ips_badoptions);
37196263Sobrien	return (1);
37296263Sobrien}
37396263Sobrien
37496263Sobrien/*
37596263Sobrien * Save incoming source route for use in replies, to be picked up later by
37696263Sobrien * ip_srcroute if the receiver is interested.
37796263Sobrien */
37896263Sobrienstatic void
37996263Sobriensave_rte(struct mbuf *m, u_char *option, struct in_addr dst)
38096263Sobrien{
38196263Sobrien	unsigned olen;
38296263Sobrien	struct ipopt_tag *opts;
38396263Sobrien
38496263Sobrien	opts = (struct ipopt_tag *)m_tag_get(PACKET_TAG_IPOPTIONS,
38596263Sobrien	    sizeof(struct ipopt_tag), M_NOWAIT);
38696263Sobrien	if (opts == NULL)
38796263Sobrien		return;
38896263Sobrien
38996263Sobrien	olen = option[IPOPT_OLEN];
39096263Sobrien	if (olen > sizeof(opts->ip_srcrt) - (1 + sizeof(dst))) {
39196263Sobrien		m_tag_free((struct m_tag *)opts);
39296263Sobrien		return;
39396263Sobrien	}
39496263Sobrien	bcopy(option, opts->ip_srcrt.srcopt, olen);
39596263Sobrien	opts->ip_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr);
39696263Sobrien	opts->ip_srcrt.dst = dst;
39796263Sobrien	m_tag_prepend(m, (struct m_tag *)opts);
39896263Sobrien}
39996263Sobrien
40096263Sobrien/*
40196263Sobrien * Retrieve incoming source route for use in replies, in the same form used
40296263Sobrien * by setsockopt.  The first hop is placed before the options, will be
40396263Sobrien * removed later.
40496263Sobrien */
40596263Sobrienstruct mbuf *
40696263Sobrienip_srcroute(struct mbuf *m0)
40796263Sobrien{
40896263Sobrien	struct in_addr *p, *q;
40996263Sobrien	struct mbuf *m;
41096263Sobrien	struct ipopt_tag *opts;
41196263Sobrien
41296263Sobrien	opts = (struct ipopt_tag *)m_tag_find(m0, PACKET_TAG_IPOPTIONS, NULL);
41396263Sobrien	if (opts == NULL)
41496263Sobrien		return (NULL);
41596263Sobrien
41696263Sobrien	if (opts->ip_nhops == 0)
41796263Sobrien		return (NULL);
41896263Sobrien	m = m_get(M_DONTWAIT, MT_DATA);
41996263Sobrien	if (m == NULL)
42096263Sobrien		return (NULL);
42196263Sobrien
42296263Sobrien#define OPTSIZ	(sizeof(opts->ip_srcrt.nop) + sizeof(opts->ip_srcrt.srcopt))
42396263Sobrien
42496263Sobrien	/* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */
42596263Sobrien	m->m_len = opts->ip_nhops * sizeof(struct in_addr) +
42696263Sobrien	    sizeof(struct in_addr) + OPTSIZ;
42796263Sobrien
42896263Sobrien	/*
42996263Sobrien	 * First, save first hop for return route.
43096263Sobrien	 */
43196263Sobrien	p = &(opts->ip_srcrt.route[opts->ip_nhops - 1]);
43296263Sobrien	*(mtod(m, struct in_addr *)) = *p--;
43396263Sobrien
43496263Sobrien	/*
43596263Sobrien	 * Copy option fields and padding (nop) to mbuf.
43696263Sobrien	 */
43796263Sobrien	opts->ip_srcrt.nop = IPOPT_NOP;
43896263Sobrien	opts->ip_srcrt.srcopt[IPOPT_OFFSET] = IPOPT_MINOFF;
43996263Sobrien	(void)memcpy(mtod(m, caddr_t) + sizeof(struct in_addr),
44096263Sobrien	    &(opts->ip_srcrt.nop), OPTSIZ);
44196263Sobrien	q = (struct in_addr *)(mtod(m, caddr_t) +
44296263Sobrien	    sizeof(struct in_addr) + OPTSIZ);
44396263Sobrien#undef OPTSIZ
44496263Sobrien	/*
44596263Sobrien	 * Record return path as an IP source route, reversing the path
44696263Sobrien	 * (pointers are now aligned).
44796263Sobrien	 */
44896263Sobrien	while (p >= opts->ip_srcrt.route) {
44996263Sobrien		*q++ = *p--;
45096263Sobrien	}
45196263Sobrien	/*
45296263Sobrien	 * Last hop goes to final destination.
45396263Sobrien	 */
45496263Sobrien	*q = opts->ip_srcrt.dst;
45596263Sobrien	m_tag_delete(m0, (struct m_tag *)opts);
45696263Sobrien	return (m);
45796263Sobrien}
45896263Sobrien
45996263Sobrien/*
46096263Sobrien * Strip out IP options, at higher level protocol in the kernel.  Second
46196263Sobrien * argument is buffer to which options will be moved, and return value is
46296263Sobrien * their length.
46396263Sobrien *
46496263Sobrien * XXX should be deleted; last arg currently ignored.
46596263Sobrien */
46696263Sobrienvoid
46796263Sobrienip_stripoptions(struct mbuf *m, struct mbuf *mopt)
46896263Sobrien{
46996263Sobrien	int i;
47096263Sobrien	struct ip *ip = mtod(m, struct ip *);
47196263Sobrien	caddr_t opts;
47296263Sobrien	int olen;
47396263Sobrien
47496263Sobrien	olen = (ip->ip_hl << 2) - sizeof (struct ip);
47596263Sobrien	opts = (caddr_t)(ip + 1);
47696263Sobrien	i = m->m_len - (sizeof (struct ip) + olen);
47796263Sobrien	bcopy(opts + olen, opts, (unsigned)i);
47896263Sobrien	m->m_len -= olen;
47996263Sobrien	if (m->m_flags & M_PKTHDR)
48096263Sobrien		m->m_pkthdr.len -= olen;
48196263Sobrien	ip->ip_v = IPVERSION;
48296263Sobrien	ip->ip_hl = sizeof(struct ip) >> 2;
48396263Sobrien}
48496263Sobrien
48596263Sobrien/*
48696263Sobrien * Insert IP options into preformed packet.  Adjust IP destination as
48796263Sobrien * required for IP source routing, as indicated by a non-zero in_addr at the
48896263Sobrien * start of the options.
48996263Sobrien *
49096263Sobrien * XXX This routine assumes that the packet has no options in place.
49196263Sobrien */
49296263Sobrienstruct mbuf *
49396263Sobrienip_insertoptions(struct mbuf *m, struct mbuf *opt, int *phlen)
49496263Sobrien{
49596263Sobrien	struct ipoption *p = mtod(opt, struct ipoption *);
49696263Sobrien	struct mbuf *n;
49796263Sobrien	struct ip *ip = mtod(m, struct ip *);
49896263Sobrien	unsigned optlen;
49996263Sobrien
50096263Sobrien	optlen = opt->m_len - sizeof(p->ipopt_dst);
50196263Sobrien	if (optlen + ip->ip_len > IP_MAXPACKET) {
50296263Sobrien		*phlen = 0;
50396263Sobrien		return (m);		/* XXX should fail */
50496263Sobrien	}
50596263Sobrien	if (p->ipopt_dst.s_addr)
50696263Sobrien		ip->ip_dst = p->ipopt_dst;
50796263Sobrien	if (m->m_flags & M_EXT || m->m_data - optlen < m->m_pktdat) {
50896263Sobrien		MGETHDR(n, M_DONTWAIT, MT_DATA);
50996263Sobrien		if (n == NULL) {
51096263Sobrien			*phlen = 0;
51196263Sobrien			return (m);
51296263Sobrien		}
51396263Sobrien		M_MOVE_PKTHDR(n, m);
51496263Sobrien		n->m_pkthdr.rcvif = NULL;
51596263Sobrien		n->m_pkthdr.len += optlen;
51696263Sobrien		m->m_len -= sizeof(struct ip);
51796263Sobrien		m->m_data += sizeof(struct ip);
51896263Sobrien		n->m_next = m;
51996263Sobrien		m = n;
52096263Sobrien		m->m_len = optlen + sizeof(struct ip);
52196263Sobrien		m->m_data += max_linkhdr;
52296263Sobrien		bcopy(ip, mtod(m, void *), sizeof(struct ip));
52396263Sobrien	} else {
52496263Sobrien		m->m_data -= optlen;
52596263Sobrien		m->m_len += optlen;
52696263Sobrien		m->m_pkthdr.len += optlen;
52796263Sobrien		bcopy(ip, mtod(m, void *), sizeof(struct ip));
52896263Sobrien	}
52996263Sobrien	ip = mtod(m, struct ip *);
53096263Sobrien	bcopy(p->ipopt_list, ip + 1, optlen);
53196263Sobrien	*phlen = sizeof(struct ip) + optlen;
53296263Sobrien	ip->ip_v = IPVERSION;
53396263Sobrien	ip->ip_hl = *phlen >> 2;
53496263Sobrien	ip->ip_len += optlen;
53596263Sobrien	return (m);
53696263Sobrien}
53796263Sobrien
53896263Sobrien/*
53996263Sobrien * Copy options from ip to jp, omitting those not copied during
54096263Sobrien * fragmentation.
54196263Sobrien */
54296263Sobrienint
54396263Sobrienip_optcopy(struct ip *ip, struct ip *jp)
54496263Sobrien{
54596263Sobrien	u_char *cp, *dp;
54696263Sobrien	int opt, optlen, cnt;
54796263Sobrien
54896263Sobrien	cp = (u_char *)(ip + 1);
54996263Sobrien	dp = (u_char *)(jp + 1);
55096263Sobrien	cnt = (ip->ip_hl << 2) - sizeof (struct ip);
55196263Sobrien	for (; cnt > 0; cnt -= optlen, cp += optlen) {
55296263Sobrien		opt = cp[0];
55396263Sobrien		if (opt == IPOPT_EOL)
55496263Sobrien			break;
55596263Sobrien		if (opt == IPOPT_NOP) {
55696263Sobrien			/* Preserve for IP mcast tunnel's LSRR alignment. */
55796263Sobrien			*dp++ = IPOPT_NOP;
55896263Sobrien			optlen = 1;
55996263Sobrien			continue;
56096263Sobrien		}
56196263Sobrien
56296263Sobrien		KASSERT(cnt >= IPOPT_OLEN + sizeof(*cp),
56396263Sobrien		    ("ip_optcopy: malformed ipv4 option"));
56496263Sobrien		optlen = cp[IPOPT_OLEN];
56596263Sobrien		KASSERT(optlen >= IPOPT_OLEN + sizeof(*cp) && optlen <= cnt,
56696263Sobrien		    ("ip_optcopy: malformed ipv4 option"));
56796263Sobrien
56896263Sobrien		/* Bogus lengths should have been caught by ip_dooptions. */
56996263Sobrien		if (optlen > cnt)
57096263Sobrien			optlen = cnt;
57196263Sobrien		if (IPOPT_COPIED(opt)) {
57296263Sobrien			bcopy(cp, dp, optlen);
57396263Sobrien			dp += optlen;
57496263Sobrien		}
57596263Sobrien	}
57696263Sobrien	for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++)
57796263Sobrien		*dp++ = IPOPT_EOL;
57896263Sobrien	return (optlen);
57996263Sobrien}
58096263Sobrien
58196263Sobrien/*
58296263Sobrien * Set up IP options in pcb for insertion in output packets.  Store in mbuf
58396263Sobrien * with pointer in pcbopt, adding pseudo-option with destination address if
58496263Sobrien * source routed.
58596263Sobrien */
58696263Sobrienint
58796263Sobrienip_pcbopts(struct inpcb *inp, int optname, struct mbuf *m)
58896263Sobrien{
58996263Sobrien	int cnt, optlen;
59096263Sobrien	u_char *cp;
59196263Sobrien	struct mbuf **pcbopt;
59296263Sobrien	u_char opt;
59396263Sobrien
59496263Sobrien	INP_WLOCK_ASSERT(inp);
59596263Sobrien
59696263Sobrien	pcbopt = &inp->inp_options;
59796263Sobrien
59896263Sobrien	/* turn off any old options */
59996263Sobrien	if (*pcbopt)
60096263Sobrien		(void)m_free(*pcbopt);
60196263Sobrien	*pcbopt = 0;
60296263Sobrien	if (m == NULL || m->m_len == 0) {
60396263Sobrien		/*
60496263Sobrien		 * Only turning off any previous options.
60596263Sobrien		 */
60696263Sobrien		if (m != NULL)
60796263Sobrien			(void)m_free(m);
60896263Sobrien		return (0);
60996263Sobrien	}
61096263Sobrien
61196263Sobrien	if (m->m_len % sizeof(int32_t))
61296263Sobrien		goto bad;
61396263Sobrien	/*
61496263Sobrien	 * IP first-hop destination address will be stored before actual
61596263Sobrien	 * options; move other options back and clear it when none present.
61696263Sobrien	 */
61796263Sobrien	if (m->m_data + m->m_len + sizeof(struct in_addr) >= &m->m_dat[MLEN])
61896263Sobrien		goto bad;
61996263Sobrien	cnt = m->m_len;
62096263Sobrien	m->m_len += sizeof(struct in_addr);
62196263Sobrien	cp = mtod(m, u_char *) + sizeof(struct in_addr);
62296263Sobrien	bcopy(mtod(m, void *), cp, (unsigned)cnt);
62396263Sobrien	bzero(mtod(m, void *), sizeof(struct in_addr));
62496263Sobrien
62596263Sobrien	for (; cnt > 0; cnt -= optlen, cp += optlen) {
62696263Sobrien		opt = cp[IPOPT_OPTVAL];
62796263Sobrien		if (opt == IPOPT_EOL)
62896263Sobrien			break;
62996263Sobrien		if (opt == IPOPT_NOP)
63096263Sobrien			optlen = 1;
63196263Sobrien		else {
63296263Sobrien			if (cnt < IPOPT_OLEN + sizeof(*cp))
63396263Sobrien				goto bad;
63496263Sobrien			optlen = cp[IPOPT_OLEN];
63596263Sobrien			if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt)
63696263Sobrien				goto bad;
63796263Sobrien		}
63896263Sobrien		switch (opt) {
63996263Sobrien
64096263Sobrien		default:
64196263Sobrien			break;
64296263Sobrien
64396263Sobrien		case IPOPT_LSRR:
64496263Sobrien		case IPOPT_SSRR:
64596263Sobrien			/*
64696263Sobrien			 * User process specifies route as:
64796263Sobrien			 *
64896263Sobrien			 *	->A->B->C->D
64996263Sobrien			 *
65096263Sobrien			 * D must be our final destination (but we can't
65196263Sobrien			 * check that since we may not have connected yet).
65296263Sobrien			 * A is first hop destination, which doesn't appear
65396263Sobrien			 * in actual IP option, but is stored before the
65496263Sobrien			 * options.
65596263Sobrien			 */
65696263Sobrien			/* XXX-BZ PRIV_NETINET_SETHDROPTS? */
65796263Sobrien			if (optlen < IPOPT_MINOFF - 1 + sizeof(struct in_addr))
65896263Sobrien				goto bad;
65996263Sobrien			m->m_len -= sizeof(struct in_addr);
66096263Sobrien			cnt -= sizeof(struct in_addr);
66196263Sobrien			optlen -= sizeof(struct in_addr);
66296263Sobrien			cp[IPOPT_OLEN] = optlen;
66396263Sobrien			/*
66496263Sobrien			 * Move first hop before start of options.
66596263Sobrien			 */
66696263Sobrien			bcopy((caddr_t)&cp[IPOPT_OFFSET+1], mtod(m, caddr_t),
66796263Sobrien			    sizeof(struct in_addr));
66896263Sobrien			/*
66996263Sobrien			 * Then copy rest of options back
67096263Sobrien			 * to close up the deleted entry.
67196263Sobrien			 */
67296263Sobrien			bcopy((&cp[IPOPT_OFFSET+1] + sizeof(struct in_addr)),
67396263Sobrien			    &cp[IPOPT_OFFSET+1],
67496263Sobrien			    (unsigned)cnt - (IPOPT_MINOFF - 1));
67596263Sobrien			break;
67696263Sobrien		}
67796263Sobrien	}
67896263Sobrien	if (m->m_len > MAX_IPOPTLEN + sizeof(struct in_addr))
67996263Sobrien		goto bad;
68096263Sobrien	*pcbopt = m;
68196263Sobrien	return (0);
68296263Sobrien
68396263Sobrienbad:
68496263Sobrien	(void)m_free(m);
68596263Sobrien	return (EINVAL);
68696263Sobrien}
68796263Sobrien
68896263Sobrien/*
68996263Sobrien * Check for the presence of the IP Router Alert option [RFC2113]
69096263Sobrien * in the header of an IPv4 datagram.
69196263Sobrien *
69296263Sobrien * This call is not intended for use from the forwarding path; it is here
69396263Sobrien * so that protocol domains may check for the presence of the option.
69496263Sobrien * Given how FreeBSD's IPv4 stack is currently structured, the Router Alert
69596263Sobrien * option does not have much relevance to the implementation, though this
69696263Sobrien * may change in future.
69796263Sobrien * Router alert options SHOULD be passed if running in IPSTEALTH mode and
69896263Sobrien * we are not the endpoint.
69996263Sobrien * Length checks on individual options should already have been peformed
70096263Sobrien * by ip_dooptions() therefore they are folded under INVARIANTS here.
70196263Sobrien *
70296263Sobrien * Return zero if not present or options are invalid, non-zero if present.
70396263Sobrien */
70496263Sobrienint
70596263Sobrienip_checkrouteralert(struct mbuf *m)
70696263Sobrien{
70796263Sobrien	struct ip *ip = mtod(m, struct ip *);
70896263Sobrien	u_char *cp;
70996263Sobrien	int opt, optlen, cnt, found_ra;
71096263Sobrien
71196263Sobrien	found_ra = 0;
71296263Sobrien	cp = (u_char *)(ip + 1);
71396263Sobrien	cnt = (ip->ip_hl << 2) - sizeof (struct ip);
71496263Sobrien	for (; cnt > 0; cnt -= optlen, cp += optlen) {
71596263Sobrien		opt = cp[IPOPT_OPTVAL];
71696263Sobrien		if (opt == IPOPT_EOL)
71796263Sobrien			break;
71896263Sobrien		if (opt == IPOPT_NOP)
71996263Sobrien			optlen = 1;
72096263Sobrien		else {
72196263Sobrien#ifdef INVARIANTS
72296263Sobrien			if (cnt < IPOPT_OLEN + sizeof(*cp))
72396263Sobrien				break;
72496263Sobrien#endif
72596263Sobrien			optlen = cp[IPOPT_OLEN];
72696263Sobrien#ifdef INVARIANTS
72796263Sobrien			if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt)
72896263Sobrien				break;
72996263Sobrien#endif
73096263Sobrien		}
73196263Sobrien		switch (opt) {
73296263Sobrien		case IPOPT_RA:
73396263Sobrien#ifdef INVARIANTS
73496263Sobrien			if (optlen != IPOPT_OFFSET + sizeof(uint16_t) ||
73596263Sobrien			    (*((uint16_t *)&cp[IPOPT_OFFSET]) != 0))
73696263Sobrien			    break;
73796263Sobrien			else
73896263Sobrien#endif
73996263Sobrien			found_ra = 1;
74096263Sobrien			break;
74196263Sobrien		default:
74296263Sobrien			break;
74396263Sobrien		}
74496263Sobrien	}
74596263Sobrien
74696263Sobrien	return (found_ra);
74796263Sobrien}
74896263Sobrien