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