Return to ip_input.c CVS log | Up to [cvs.NetBSD.org] / src / sys / netinet |
Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. |
version 1.159, 2002/11/02 07:28:12 | version 1.217, 2005/06/09 02:19:59 | ||
---|---|---|---|
|
|
||
* 2. Redistributions in binary form must reproduce the above copyright | * 2. Redistributions in binary form must reproduce the above copyright | ||
* notice, this list of conditions and the following disclaimer in the | * notice, this list of conditions and the following disclaimer in the | ||
* documentation and/or other materials provided with the distribution. | * documentation and/or other materials provided with the distribution. | ||
* 3. All advertising materials mentioning features or use of this software | * 3. Neither the name of the University nor the names of its contributors | ||
* must display the following acknowledgement: | |||
* This product includes software developed by the University of | |||
* California, Berkeley and its contributors. | |||
* 4. Neither the name of the University nor the names of its contributors | |||
* may be used to endorse or promote products derived from this software | * may be used to endorse or promote products derived from this software | ||
* without specific prior written permission. | * without specific prior written permission. | ||
* | * | ||
|
|
||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||
__KERNEL_RCSID(0, "$NetBSD$"); | __KERNEL_RCSID(0, "$NetBSD$"); | ||
#include "opt_inet.h" | |||
#include "opt_gateway.h" | #include "opt_gateway.h" | ||
#include "opt_pfil_hooks.h" | #include "opt_pfil_hooks.h" | ||
#include "opt_ipsec.h" | #include "opt_ipsec.h" | ||
#include "opt_mrouting.h" | #include "opt_mrouting.h" | ||
#include "opt_mbuftrace.h" | |||
#include "opt_inet_csum.h" | #include "opt_inet_csum.h" | ||
#include <sys/param.h> | #include <sys/param.h> | ||
|
|
||
#include <netinet/in_systm.h> | #include <netinet/in_systm.h> | ||
#include <netinet/ip.h> | #include <netinet/ip.h> | ||
#include <netinet/in_pcb.h> | #include <netinet/in_pcb.h> | ||
#include <netinet/in_proto.h> | |||
#include <netinet/in_var.h> | #include <netinet/in_var.h> | ||
#include <netinet/ip_var.h> | #include <netinet/ip_var.h> | ||
#include <netinet/ip_icmp.h> | #include <netinet/ip_icmp.h> | ||
|
|
||
#include <netinet6/ipsec.h> | #include <netinet6/ipsec.h> | ||
#include <netkey/key.h> | #include <netkey/key.h> | ||
#endif | #endif | ||
#ifdef FAST_IPSEC | |||
#include <netipsec/ipsec.h> | |||
#include <netipsec/key.h> | |||
#endif /* FAST_IPSEC*/ | |||
#ifndef IPFORWARDING | #ifndef IPFORWARDING | ||
#ifdef GATEWAY | #ifdef GATEWAY | ||
|
|
||
int ipprintfs = 0; | int ipprintfs = 0; | ||
#endif | #endif | ||
int ip_do_randomid = 0; | |||
/* | |||
* XXX - Setting ip_checkinterface mostly implements the receive side of | |||
* the Strong ES model described in RFC 1122, but since the routing table | |||
* and transmit implementation do not implement the Strong ES model, | |||
* setting this to 1 results in an odd hybrid. | |||
* | |||
* XXX - ip_checkinterface currently must be disabled if you use ipnat | |||
* to translate the destination address to another local interface. | |||
* | |||
* XXX - ip_checkinterface must be disabled if you add IP aliases | |||
* to the loopback interface instead of the interface where the | |||
* packets for those addresses are received. | |||
*/ | |||
int ip_checkinterface = 0; | |||
struct rttimer_queue *ip_mtudisc_timeout_q = NULL; | struct rttimer_queue *ip_mtudisc_timeout_q = NULL; | ||
extern struct domain inetdomain; | |||
int ipqmaxlen = IFQ_MAXLEN; | int ipqmaxlen = IFQ_MAXLEN; | ||
u_long in_ifaddrhash; /* size of hash table - 1 */ | u_long in_ifaddrhash; /* size of hash table - 1 */ | ||
int in_ifaddrentries; /* total number of addrs */ | int in_ifaddrentries; /* total number of addrs */ | ||
struct in_ifaddrhead in_ifaddr; | struct in_ifaddrhead in_ifaddrhead; | ||
struct in_ifaddrhashhead *in_ifaddrhashtbl; | struct in_ifaddrhashhead *in_ifaddrhashtbl; | ||
u_long in_multihash; /* size of hash table - 1 */ | |||
int in_multientries; /* total number of addrs */ | |||
struct in_multihashhead *in_multihashtbl; | |||
struct ifqueue ipintrq; | struct ifqueue ipintrq; | ||
struct ipstat ipstat; | struct ipstat ipstat; | ||
u_int16_t ip_id; | uint16_t ip_id; | ||
#ifdef PFIL_HOOKS | #ifdef PFIL_HOOKS | ||
struct pfil_head inet_pfil_hook; | struct pfil_head inet_pfil_hook; | ||
#endif | #endif | ||
struct ipqhead ipq; | /* | ||
* Cached copy of nmbclusters. If nbclusters is different, | |||
* recalculate IP parameters derived from nmbclusters. | |||
*/ | |||
static int ip_nmbclusters; /* copy of nmbclusters */ | |||
static void ip_nmbclusters_changed(void); /* recalc limits */ | |||
#define CHECK_NMBCLUSTER_PARAMS() \ | |||
do { \ | |||
if (__predict_false(ip_nmbclusters != nmbclusters)) \ | |||
ip_nmbclusters_changed(); \ | |||
} while (/*CONSTCOND*/0) | |||
/* IP datagram reassembly queues (hashed) */ | |||
#define IPREASS_NHASH_LOG2 6 | |||
#define IPREASS_NHASH (1 << IPREASS_NHASH_LOG2) | |||
#define IPREASS_HMASK (IPREASS_NHASH - 1) | |||
#define IPREASS_HASH(x,y) \ | |||
(((((x) & 0xF) | ((((x) >> 8) & 0xF) << 4)) ^ (y)) & IPREASS_HMASK) | |||
struct ipqhead ipq[IPREASS_NHASH]; | |||
int ipq_locked; | int ipq_locked; | ||
int ip_nfragpackets = 0; | static int ip_nfragpackets; /* packets in reass queue */ | ||
int ip_maxfragpackets = 200; | static int ip_nfrags; /* total fragments in reass queues */ | ||
int ip_maxfragpackets = 200; /* limit on packets. XXX sysctl */ | |||
int ip_maxfrags; /* limit on fragments. XXX sysctl */ | |||
/* | |||
* Additive-Increase/Multiplicative-Decrease (AIMD) strategy for | |||
* IP reassembly queue buffer managment. | |||
* | |||
* We keep a count of total IP fragments (NB: not fragmented packets!) | |||
* awaiting reassembly (ip_nfrags) and a limit (ip_maxfrags) on fragments. | |||
* If ip_nfrags exceeds ip_maxfrags the limit, we drop half the | |||
* total fragments in reassembly queues.This AIMD policy avoids | |||
* repeatedly deleting single packets under heavy fragmentation load | |||
* (e.g., from lossy NFS peers). | |||
*/ | |||
static u_int ip_reass_ttl_decr(u_int ticks); | |||
static void ip_reass_drophalf(void); | |||
static __inline int ipq_lock_try __P((void)); | |||
static __inline void ipq_unlock __P((void)); | static __inline int ipq_lock_try(void); | ||
static __inline void ipq_unlock(void); | |||
static __inline int | static __inline int | ||
ipq_lock_try() | ipq_lock_try(void) | ||
{ | { | ||
int s; | int s; | ||
|
|
||
} | } | ||
static __inline void | static __inline void | ||
ipq_unlock() | ipq_unlock(void) | ||
{ | { | ||
int s; | int s; | ||
|
|
||
#define IPQ_UNLOCK() ipq_unlock() | #define IPQ_UNLOCK() ipq_unlock() | ||
struct pool ipqent_pool; | POOL_INIT(inmulti_pool, sizeof(struct in_multi), 0, 0, 0, "inmltpl", NULL); | ||
POOL_INIT(ipqent_pool, sizeof(struct ipqent), 0, 0, 0, "ipqepl", NULL); | |||
#ifdef INET_CSUM_COUNTERS | #ifdef INET_CSUM_COUNTERS | ||
#include <sys/device.h> | #include <sys/device.h> | ||
|
|
||
#define INET_CSUM_COUNTER_INCR(ev) (ev)->ev_count++ | #define INET_CSUM_COUNTER_INCR(ev) (ev)->ev_count++ | ||
EVCNT_ATTACH_STATIC(ip_hwcsum_bad); | |||
EVCNT_ATTACH_STATIC(ip_hwcsum_ok); | |||
EVCNT_ATTACH_STATIC(ip_swcsum); | |||
#else | #else | ||
#define INET_CSUM_COUNTER_INCR(ev) /* nothing */ | #define INET_CSUM_COUNTER_INCR(ev) /* nothing */ | ||
|
|
||
struct in_addr route[MAX_IPOPTLEN/sizeof(struct in_addr)]; | struct in_addr route[MAX_IPOPTLEN/sizeof(struct in_addr)]; | ||
} ip_srcrt; | } ip_srcrt; | ||
static void save_rte __P((u_char *, struct in_addr)); | static void save_rte(u_char *, struct in_addr); | ||
#ifdef MBUFTRACE | |||
struct mowner ip_rx_mowner = { "internet", "rx" }; | |||
struct mowner ip_tx_mowner = { "internet", "tx" }; | |||
#endif | |||
/* | |||
* Compute IP limits derived from the value of nmbclusters. | |||
*/ | |||
static void | |||
ip_nmbclusters_changed(void) | |||
{ | |||
ip_maxfrags = nmbclusters / 4; | |||
ip_nmbclusters = nmbclusters; | |||
} | |||
/* | /* | ||
* IP initialization: fill in IP protocol switch table. | * IP initialization: fill in IP protocol switch table. | ||
* All protocols not implemented in kernel go to raw IP protocol handler. | * All protocols not implemented in kernel go to raw IP protocol handler. | ||
*/ | */ | ||
void | void | ||
ip_init() | ip_init(void) | ||
{ | { | ||
struct protosw *pr; | const struct protosw *pr; | ||
int i; | int i; | ||
pool_init(&ipqent_pool, sizeof(struct ipqent), 0, 0, 0, "ipqepl", | |||
NULL); | |||
pr = pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW); | pr = pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW); | ||
if (pr == 0) | if (pr == 0) | ||
panic("ip_init"); | panic("ip_init"); | ||
|
|
||
if (pr->pr_domain->dom_family == PF_INET && | if (pr->pr_domain->dom_family == PF_INET && | ||
pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW) | pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW) | ||
ip_protox[pr->pr_protocol] = pr - inetsw; | ip_protox[pr->pr_protocol] = pr - inetsw; | ||
LIST_INIT(&ipq); | |||
ip_id = time.tv_sec & 0xffff; | for (i = 0; i < IPREASS_NHASH; i++) | ||
LIST_INIT(&ipq[i]); | |||
ip_id = time.tv_sec & 0xfffff; | |||
ipintrq.ifq_maxlen = ipqmaxlen; | ipintrq.ifq_maxlen = ipqmaxlen; | ||
TAILQ_INIT(&in_ifaddr); | ip_nmbclusters_changed(); | ||
TAILQ_INIT(&in_ifaddrhead); | |||
in_ifaddrhashtbl = hashinit(IN_IFADDR_HASH_SIZE, HASH_LIST, M_IFADDR, | in_ifaddrhashtbl = hashinit(IN_IFADDR_HASH_SIZE, HASH_LIST, M_IFADDR, | ||
M_WAITOK, &in_ifaddrhash); | M_WAITOK, &in_ifaddrhash); | ||
if (ip_mtudisc != 0) | in_multihashtbl = hashinit(IN_IFADDR_HASH_SIZE, HASH_LIST, M_IPMADDR, | ||
ip_mtudisc_timeout_q = | M_WAITOK, &in_multihash); | ||
rt_timer_queue_create(ip_mtudisc_timeout); | ip_mtudisc_timeout_q = rt_timer_queue_create(ip_mtudisc_timeout); | ||
#ifdef GATEWAY | #ifdef GATEWAY | ||
ipflow_init(); | ipflow_init(); | ||
#endif | #endif | ||
|
|
||
"error %d\n", i); | "error %d\n", i); | ||
#endif /* PFIL_HOOKS */ | #endif /* PFIL_HOOKS */ | ||
#ifdef INET_CSUM_COUNTERS | #ifdef MBUFTRACE | ||
evcnt_attach_static(&ip_hwcsum_bad); | MOWNER_ATTACH(&ip_tx_mowner); | ||
evcnt_attach_static(&ip_hwcsum_ok); | MOWNER_ATTACH(&ip_rx_mowner); | ||
evcnt_attach_static(&ip_swcsum); | #endif /* MBUFTRACE */ | ||
#endif /* INET_CSUM_COUNTERS */ | |||
} | } | ||
struct sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET }; | struct sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET }; | ||
|
|
||
* IP software interrupt routine | * IP software interrupt routine | ||
*/ | */ | ||
void | void | ||
ipintr() | ipintr(void) | ||
{ | { | ||
int s; | int s; | ||
struct mbuf *m; | struct mbuf *m; | ||
|
|
||
splx(s); | splx(s); | ||
if (m == 0) | if (m == 0) | ||
return; | return; | ||
MCLAIM(m, &ip_rx_mowner); | |||
ip_input(m); | ip_input(m); | ||
} | } | ||
} | } | ||
|
|
||
struct ipqent *ipqe; | struct ipqent *ipqe; | ||
int hlen = 0, mff, len; | int hlen = 0, mff, len; | ||
int downmatch; | int downmatch; | ||
int checkif; | |||
int srcrt = 0; | |||
u_int hash; | |||
#ifdef FAST_IPSEC | |||
struct m_tag *mtag; | |||
struct tdb_ident *tdbi; | |||
struct secpolicy *sp; | |||
int s, error; | |||
#endif /* FAST_IPSEC */ | |||
MCLAIM(m, &ip_rx_mowner); | |||
#ifdef DIAGNOSTIC | #ifdef DIAGNOSTIC | ||
if ((m->m_flags & M_PKTHDR) == 0) | if ((m->m_flags & M_PKTHDR) == 0) | ||
panic("ipintr no HDR"); | panic("ipintr no HDR"); | ||
#endif | #endif | ||
#ifdef IPSEC | |||
/* | |||
* should the inner packet be considered authentic? | |||
* see comment in ah4_input(). | |||
*/ | |||
if (m) { | |||
m->m_flags &= ~M_AUTHIPHDR; | |||
m->m_flags &= ~M_AUTHIPDGM; | |||
} | |||
#endif | |||
/* | /* | ||
* If no IP addresses have been set yet but the interfaces | * If no IP addresses have been set yet but the interfaces | ||
* are receiving, can't do anything with incoming packets yet. | * are receiving, can't do anything with incoming packets yet. | ||
*/ | */ | ||
if (TAILQ_FIRST(&in_ifaddr) == 0) | if (TAILQ_FIRST(&in_ifaddrhead) == 0) | ||
goto bad; | goto bad; | ||
ipstat.ips_total++; | ipstat.ips_total++; | ||
/* | /* | ||
|
|
||
break; | break; | ||
default: | default: | ||
/* Must compute it ourselves. */ | /* | ||
INET_CSUM_COUNTER_INCR(&ip_swcsum); | * Must compute it ourselves. Maybe skip checksum on | ||
if (in_cksum(m, hlen) != 0) | * loopback interfaces. | ||
goto bad; | */ | ||
if (__predict_true(!(m->m_pkthdr.rcvif->if_flags & | |||
IFF_LOOPBACK) || ip_do_loopback_cksum)) { | |||
INET_CSUM_COUNTER_INCR(&ip_swcsum); | |||
if (in_cksum(m, hlen) != 0) | |||
goto badcsum; | |||
} | |||
break; | break; | ||
} | } | ||
|
|
||
m_adj(m, len - m->m_pkthdr.len); | m_adj(m, len - m->m_pkthdr.len); | ||
} | } | ||
#ifdef IPSEC | #if defined(IPSEC) | ||
/* ipflow (IP fast forwarding) is not compatible with IPsec. */ | /* ipflow (IP fast forwarding) is not compatible with IPsec. */ | ||
m->m_flags &= ~M_CANFASTFWD; | m->m_flags &= ~M_CANFASTFWD; | ||
#else | #else | ||
|
|
||
*/ | */ | ||
#ifdef IPSEC | #ifdef IPSEC | ||
if (!ipsec_getnhist(m)) | if (!ipsec_getnhist(m)) | ||
#elif defined(FAST_IPSEC) | |||
if (!ipsec_indone(m)) | |||
#else | #else | ||
if (1) | if (1) | ||
#endif | #endif | ||
{ | { | ||
struct in_addr odst; | |||
odst = ip->ip_dst; | |||
if (pfil_run_hooks(&inet_pfil_hook, &m, m->m_pkthdr.rcvif, | if (pfil_run_hooks(&inet_pfil_hook, &m, m->m_pkthdr.rcvif, | ||
PFIL_IN) != 0) | PFIL_IN) != 0) | ||
return; | return; | ||
if (m == NULL) | if (m == NULL) | ||
return; | return; | ||
ip = mtod(m, struct ip *); | ip = mtod(m, struct ip *); | ||
hlen = ip->ip_hl << 2; | hlen = ip->ip_hl << 2; | ||
/* | |||
* XXX The setting of "srcrt" here is to prevent ip_forward() | |||
* from generating ICMP redirects for packets that have | |||
* been redirected by a hook back out on to the same LAN that | |||
* they came from and is not an indication that the packet | |||
* is being inffluenced by source routing options. This | |||
* allows things like | |||
* "rdr tlp0 0/0 port 80 -> 1.1.1.200 3128 tcp" | |||
* where tlp0 is both on the 1.1.1.0/24 network and is the | |||
* default route for hosts on 1.1.1.0/24. Of course this | |||
* also requires a "map tlp0 ..." to complete the story. | |||
* One might argue whether or not this kind of network config. | |||
* should be supported in this manner... | |||
*/ | |||
srcrt = (odst.s_addr != ip->ip_dst.s_addr); | |||
} | } | ||
#endif /* PFIL_HOOKS */ | #endif /* PFIL_HOOKS */ | ||
|
|
||
return; | return; | ||
/* | /* | ||
* Enable a consistency check between the destination address | |||
* and the arrival interface for a unicast packet (the RFC 1122 | |||
* strong ES model) if IP forwarding is disabled and the packet | |||
* is not locally generated. | |||
* | |||
* XXX - Checking also should be disabled if the destination | |||
* address is ipnat'ed to a different interface. | |||
* | |||
* XXX - Checking is incompatible with IP aliases added | |||
* to the loopback interface instead of the interface where | |||
* the packets are received. | |||
* | |||
* XXX - We need to add a per ifaddr flag for this so that | |||
* we get finer grain control. | |||
*/ | |||
checkif = ip_checkinterface && (ipforwarding == 0) && | |||
(m->m_pkthdr.rcvif != NULL) && | |||
((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0); | |||
/* | |||
* Check our list of addresses, to see if the packet is for us. | * Check our list of addresses, to see if the packet is for us. | ||
* | * | ||
* Traditional 4.4BSD did not consult IFF_UP at all. | * Traditional 4.4BSD did not consult IFF_UP at all. | ||
|
|
||
downmatch = 0; | downmatch = 0; | ||
LIST_FOREACH(ia, &IN_IFADDR_HASH(ip->ip_dst.s_addr), ia_hash) { | LIST_FOREACH(ia, &IN_IFADDR_HASH(ip->ip_dst.s_addr), ia_hash) { | ||
if (in_hosteq(ia->ia_addr.sin_addr, ip->ip_dst)) { | if (in_hosteq(ia->ia_addr.sin_addr, ip->ip_dst)) { | ||
if (checkif && ia->ia_ifp != m->m_pkthdr.rcvif) | |||
continue; | |||
if ((ia->ia_ifp->if_flags & IFF_UP) != 0) | if ((ia->ia_ifp->if_flags & IFF_UP) != 0) | ||
break; | break; | ||
else | else | ||
|
|
||
if (ia != NULL) | if (ia != NULL) | ||
goto ours; | goto ours; | ||
if (m->m_pkthdr.rcvif->if_flags & IFF_BROADCAST) { | if (m->m_pkthdr.rcvif->if_flags & IFF_BROADCAST) { | ||
TAILQ_FOREACH(ifa, &m->m_pkthdr.rcvif->if_addrlist, ifa_list) { | IFADDR_FOREACH(ifa, m->m_pkthdr.rcvif) { | ||
if (ifa->ifa_addr->sa_family != AF_INET) | if (ifa->ifa_addr->sa_family != AF_INET) | ||
continue; | continue; | ||
ia = ifatoia(ifa); | ia = ifatoia(ifa); | ||
|
|
||
#ifdef MROUTING | #ifdef MROUTING | ||
extern struct socket *ip_mrouter; | extern struct socket *ip_mrouter; | ||
if (M_READONLY(m)) { | |||
if ((m = m_pullup(m, hlen)) == 0) { | |||
ipstat.ips_toosmall++; | |||
return; | |||
} | |||
ip = mtod(m, struct ip *); | |||
} | |||
if (ip_mrouter) { | if (ip_mrouter) { | ||
/* | /* | ||
* If we are acting as a multicast router, all | * If we are acting as a multicast router, all | ||
|
|
||
goto bad; | goto bad; | ||
} | } | ||
#endif | #endif | ||
#ifdef FAST_IPSEC | |||
mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL); | |||
s = splsoftnet(); | |||
if (mtag != NULL) { | |||
tdbi = (struct tdb_ident *)(mtag + 1); | |||
sp = ipsec_getpolicy(tdbi, IPSEC_DIR_INBOUND); | |||
} else { | |||
sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_INBOUND, | |||
IP_FORWARDING, &error); | |||
} | |||
if (sp == NULL) { /* NB: can happen if error */ | |||
splx(s); | |||
/*XXX error stat???*/ | |||
DPRINTF(("ip_input: no SP for forwarding\n")); /*XXX*/ | |||
goto bad; | |||
} | |||
/* | |||
* Check security policy against packet attributes. | |||
*/ | |||
error = ipsec_in_reject(sp, m); | |||
KEY_FREESP(&sp); | |||
splx(s); | |||
if (error) { | |||
ipstat.ips_cantforward++; | |||
goto bad; | |||
} | |||
ip_forward(m, 0); | /* | ||
* Peek at the outbound SP for this packet to determine if | |||
* it's a Fast Forward candidate. | |||
*/ | |||
mtag = m_tag_find(m, PACKET_TAG_IPSEC_PENDING_TDB, NULL); | |||
if (mtag != NULL) | |||
m->m_flags &= ~M_CANFASTFWD; | |||
else { | |||
s = splsoftnet(); | |||
sp = ipsec4_checkpolicy(m, IPSEC_DIR_OUTBOUND, | |||
(IP_FORWARDING | | |||
(ip_directedbcast ? IP_ALLOWBROADCAST : 0)), | |||
&error, NULL); | |||
if (sp != NULL) { | |||
m->m_flags &= ~M_CANFASTFWD; | |||
KEY_FREESP(&sp); | |||
} | |||
splx(s); | |||
} | |||
#endif /* FAST_IPSEC */ | |||
ip_forward(m, srcrt); | |||
} | } | ||
return; | return; | ||
|
|
||
* but it's not worth the time; just let them time out.) | * but it's not worth the time; just let them time out.) | ||
*/ | */ | ||
if (ip->ip_off & ~htons(IP_DF|IP_RF)) { | if (ip->ip_off & ~htons(IP_DF|IP_RF)) { | ||
if (M_READONLY(m)) { | |||
if ((m = m_pullup(m, hlen)) == NULL) { | |||
ipstat.ips_toosmall++; | |||
goto bad; | |||
} | |||
ip = mtod(m, struct ip *); | |||
} | |||
/* | /* | ||
* Look for queue of fragments | * Look for queue of fragments | ||
* of this datagram. | * of this datagram. | ||
*/ | */ | ||
IPQ_LOCK(); | IPQ_LOCK(); | ||
LIST_FOREACH(fp, &ipq, ipq_q) | hash = IPREASS_HASH(ip->ip_src.s_addr, ip->ip_id); | ||
/* XXX LIST_FOREACH(fp, &ipq[hash], ipq_q) */ | |||
for (fp = LIST_FIRST(&ipq[hash]); fp != NULL; | |||
fp = LIST_NEXT(fp, ipq_q)) { | |||
if (ip->ip_id == fp->ipq_id && | if (ip->ip_id == fp->ipq_id && | ||
in_hosteq(ip->ip_src, fp->ipq_src) && | in_hosteq(ip->ip_src, fp->ipq_src) && | ||
in_hosteq(ip->ip_dst, fp->ipq_dst) && | in_hosteq(ip->ip_dst, fp->ipq_dst) && | ||
ip->ip_p == fp->ipq_p) | ip->ip_p == fp->ipq_p) | ||
goto found; | goto found; | ||
} | |||
fp = 0; | fp = 0; | ||
found: | found: | ||
|
|
||
ipqe->ipqe_mff = mff; | ipqe->ipqe_mff = mff; | ||
ipqe->ipqe_m = m; | ipqe->ipqe_m = m; | ||
ipqe->ipqe_ip = ip; | ipqe->ipqe_ip = ip; | ||
m = ip_reass(ipqe, fp); | m = ip_reass(ipqe, fp, &ipq[hash]); | ||
if (m == 0) { | if (m == 0) { | ||
IPQ_UNLOCK(); | IPQ_UNLOCK(); | ||
return; | return; | ||
|
|
||
IPQ_UNLOCK(); | IPQ_UNLOCK(); | ||
} | } | ||
#ifdef IPSEC | #if defined(IPSEC) | ||
/* | /* | ||
* enforce IPsec policy checking if we are seeing last header. | * enforce IPsec policy checking if we are seeing last header. | ||
* note that we do not visit this with protocols with pcb layer | * note that we do not visit this with protocols with pcb layer | ||
|
|
||
goto bad; | goto bad; | ||
} | } | ||
#endif | #endif | ||
#if FAST_IPSEC | |||
/* | |||
* enforce IPsec policy checking if we are seeing last header. | |||
* note that we do not visit this with protocols with pcb layer | |||
* code - like udp/tcp/raw ip. | |||
*/ | |||
if ((inetsw[ip_protox[ip->ip_p]].pr_flags & PR_LASTHDR) != 0) { | |||
/* | |||
* Check if the packet has already had IPsec processing | |||
* done. If so, then just pass it along. This tag gets | |||
* set during AH, ESP, etc. input handling, before the | |||
* packet is returned to the ip input queue for delivery. | |||
*/ | |||
mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL); | |||
s = splsoftnet(); | |||
if (mtag != NULL) { | |||
tdbi = (struct tdb_ident *)(mtag + 1); | |||
sp = ipsec_getpolicy(tdbi, IPSEC_DIR_INBOUND); | |||
} else { | |||
sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_INBOUND, | |||
IP_FORWARDING, &error); | |||
} | |||
if (sp != NULL) { | |||
/* | |||
* Check security policy against packet attributes. | |||
*/ | |||
error = ipsec_in_reject(sp, m); | |||
KEY_FREESP(&sp); | |||
} else { | |||
/* XXX error stat??? */ | |||
error = EINVAL; | |||
DPRINTF(("ip_input: no SP, packet discarded\n"));/*XXX*/ | |||
goto bad; | |||
} | |||
splx(s); | |||
if (error) | |||
goto bad; | |||
} | |||
#endif /* FAST_IPSEC */ | |||
/* | /* | ||
* Switch out to protocol's input routine. | * Switch out to protocol's input routine. | ||
|
|
||
* is given as fp; otherwise have to make a chain. | * is given as fp; otherwise have to make a chain. | ||
*/ | */ | ||
struct mbuf * | struct mbuf * | ||
ip_reass(ipqe, fp) | ip_reass(struct ipqent *ipqe, struct ipq *fp, struct ipqhead *ipqhead) | ||
struct ipqent *ipqe; | |||
struct ipq *fp; | |||
{ | { | ||
struct mbuf *m = ipqe->ipqe_m; | struct mbuf *m = ipqe->ipqe_m; | ||
struct ipqent *nq, *p, *q; | struct ipqent *nq, *p, *q; | ||
|
|
||
m->m_data += hlen; | m->m_data += hlen; | ||
m->m_len -= hlen; | m->m_len -= hlen; | ||
#ifdef notyet | |||
/* make sure fragment limit is up-to-date */ | |||
CHECK_NMBCLUSTER_PARAMS(); | |||
/* If we have too many fragments, drop the older half. */ | |||
if (ip_nfrags >= ip_maxfrags) | |||
ip_reass_drophalf(void); | |||
#endif | |||
/* | |||
* We are about to add a fragment; increment frag count. | |||
*/ | |||
ip_nfrags++; | |||
/* | /* | ||
* If first fragment to arrive, create a reassembly queue. | * If first fragment to arrive, create a reassembly queue. | ||
*/ | */ | ||
|
|
||
M_FTABLE, M_NOWAIT); | M_FTABLE, M_NOWAIT); | ||
if (fp == NULL) | if (fp == NULL) | ||
goto dropfrag; | goto dropfrag; | ||
LIST_INSERT_HEAD(&ipq, fp, ipq_q); | LIST_INSERT_HEAD(ipqhead, fp, ipq_q); | ||
fp->ipq_nfrags = 1; | |||
fp->ipq_ttl = IPFRAGTTL; | fp->ipq_ttl = IPFRAGTTL; | ||
fp->ipq_p = ipqe->ipqe_ip->ip_p; | fp->ipq_p = ipqe->ipqe_ip->ip_p; | ||
fp->ipq_id = ipqe->ipqe_ip->ip_id; | fp->ipq_id = ipqe->ipqe_ip->ip_id; | ||
|
|
||
fp->ipq_dst = ipqe->ipqe_ip->ip_dst; | fp->ipq_dst = ipqe->ipqe_ip->ip_dst; | ||
p = NULL; | p = NULL; | ||
goto insert; | goto insert; | ||
} else { | |||
fp->ipq_nfrags++; | |||
} | } | ||
/* | /* | ||
|
|
||
m_freem(q->ipqe_m); | m_freem(q->ipqe_m); | ||
TAILQ_REMOVE(&fp->ipq_fragq, q, ipqe_q); | TAILQ_REMOVE(&fp->ipq_fragq, q, ipqe_q); | ||
pool_put(&ipqent_pool, q); | pool_put(&ipqent_pool, q); | ||
fp->ipq_nfrags--; | |||
ip_nfrags--; | |||
} | } | ||
insert: | insert: | ||
|
|
||
pool_put(&ipqent_pool, q); | pool_put(&ipqent_pool, q); | ||
m_cat(m, t); | m_cat(m, t); | ||
} | } | ||
ip_nfrags -= fp->ipq_nfrags; | |||
/* | /* | ||
* Create header for new ip packet by | * Create header for new ip packet by | ||
|
|
||
for (t = m; t; t = t->m_next) | for (t = m; t; t = t->m_next) | ||
plen += t->m_len; | plen += t->m_len; | ||
m->m_pkthdr.len = plen; | m->m_pkthdr.len = plen; | ||
m->m_pkthdr.csum_flags = 0; | |||
} | } | ||
return (m); | return (m); | ||
dropfrag: | dropfrag: | ||
if (fp != 0) | |||
fp->ipq_nfrags--; | |||
ip_nfrags--; | |||
ipstat.ips_fragdropped++; | ipstat.ips_fragdropped++; | ||
m_freem(m); | m_freem(m); | ||
pool_put(&ipqent_pool, ipqe); | pool_put(&ipqent_pool, ipqe); | ||
|
|
||
* associated datagrams. | * associated datagrams. | ||
*/ | */ | ||
void | void | ||
ip_freef(fp) | ip_freef(struct ipq *fp) | ||
struct ipq *fp; | |||
{ | { | ||
struct ipqent *q, *p; | struct ipqent *q, *p; | ||
u_int nfrags = 0; | |||
IPQ_LOCK_CHECK(); | IPQ_LOCK_CHECK(); | ||
for (q = TAILQ_FIRST(&fp->ipq_fragq); q != NULL; q = p) { | for (q = TAILQ_FIRST(&fp->ipq_fragq); q != NULL; q = p) { | ||
p = TAILQ_NEXT(q, ipqe_q); | p = TAILQ_NEXT(q, ipqe_q); | ||
m_freem(q->ipqe_m); | m_freem(q->ipqe_m); | ||
nfrags++; | |||
TAILQ_REMOVE(&fp->ipq_fragq, q, ipqe_q); | TAILQ_REMOVE(&fp->ipq_fragq, q, ipqe_q); | ||
pool_put(&ipqent_pool, q); | pool_put(&ipqent_pool, q); | ||
} | } | ||
if (nfrags != fp->ipq_nfrags) | |||
printf("ip_freef: nfrags %d != %d\n", fp->ipq_nfrags, nfrags); | |||
ip_nfrags -= nfrags; | |||
LIST_REMOVE(fp, ipq_q); | LIST_REMOVE(fp, ipq_q); | ||
FREE(fp, M_FTABLE); | FREE(fp, M_FTABLE); | ||
ip_nfragpackets--; | ip_nfragpackets--; | ||
} | } | ||
/* | /* | ||
* IP reassembly TTL machinery for multiplicative drop. | |||
*/ | |||
static u_int fragttl_histo[(IPFRAGTTL+1)]; | |||
/* | |||
* Decrement TTL of all reasembly queue entries by `ticks'. | |||
* Count number of distinct fragments (as opposed to partial, fragmented | |||
* datagrams) in the reassembly queue. While we traverse the entire | |||
* reassembly queue, compute and return the median TTL over all fragments. | |||
*/ | |||
static u_int | |||
ip_reass_ttl_decr(u_int ticks) | |||
{ | |||
u_int nfrags, median, dropfraction, keepfraction; | |||
struct ipq *fp, *nfp; | |||
int i; | |||
nfrags = 0; | |||
memset(fragttl_histo, 0, sizeof fragttl_histo); | |||
for (i = 0; i < IPREASS_NHASH; i++) { | |||
for (fp = LIST_FIRST(&ipq[i]); fp != NULL; fp = nfp) { | |||
fp->ipq_ttl = ((fp->ipq_ttl <= ticks) ? | |||
0 : fp->ipq_ttl - ticks); | |||
nfp = LIST_NEXT(fp, ipq_q); | |||
if (fp->ipq_ttl == 0) { | |||
ipstat.ips_fragtimeout++; | |||
ip_freef(fp); | |||
} else { | |||
nfrags += fp->ipq_nfrags; | |||
fragttl_histo[fp->ipq_ttl] += fp->ipq_nfrags; | |||
} | |||
} | |||
} | |||
KASSERT(ip_nfrags == nfrags); | |||
/* Find median (or other drop fraction) in histogram. */ | |||
dropfraction = (ip_nfrags / 2); | |||
keepfraction = ip_nfrags - dropfraction; | |||
for (i = IPFRAGTTL, median = 0; i >= 0; i--) { | |||
median += fragttl_histo[i]; | |||
if (median >= keepfraction) | |||
break; | |||
} | |||
/* Return TTL of median (or other fraction). */ | |||
return (u_int)i; | |||
} | |||
void | |||
ip_reass_drophalf(void) | |||
{ | |||
u_int median_ticks; | |||
/* | |||
* Compute median TTL of all fragments, and count frags | |||
* with that TTL or lower (roughly half of all fragments). | |||
*/ | |||
median_ticks = ip_reass_ttl_decr(0); | |||
/* Drop half. */ | |||
median_ticks = ip_reass_ttl_decr(median_ticks); | |||
} | |||
/* | |||
* IP timer processing; | * IP timer processing; | ||
* if a timer expires on a reassembly | * if a timer expires on a reassembly | ||
* queue, discard it. | * queue, discard it. | ||
*/ | */ | ||
void | void | ||
ip_slowtimo() | ip_slowtimo(void) | ||
{ | { | ||
struct ipq *fp, *nfp; | static u_int dropscanidx = 0; | ||
u_int i; | |||
u_int median_ttl; | |||
int s = splsoftnet(); | int s = splsoftnet(); | ||
IPQ_LOCK(); | IPQ_LOCK(); | ||
for (fp = LIST_FIRST(&ipq); fp != NULL; fp = nfp) { | |||
nfp = LIST_NEXT(fp, ipq_q); | /* Age TTL of all fragments by 1 tick .*/ | ||
if (--fp->ipq_ttl == 0) { | median_ttl = ip_reass_ttl_decr(1); | ||
ipstat.ips_fragtimeout++; | |||
ip_freef(fp); | /* make sure fragment limit is up-to-date */ | ||
} | CHECK_NMBCLUSTER_PARAMS(); | ||
} | |||
/* If we have too many fragments, drop the older half. */ | |||
if (ip_nfrags > ip_maxfrags) | |||
ip_reass_ttl_decr(median_ttl); | |||
/* | /* | ||
* If we are over the maximum number of fragments | * If we are over the maximum number of fragmented packets | ||
* (due to the limit being lowered), drain off | * (due to the limit being lowered), drain off | ||
* enough to get down to the new limit. | * enough to get down to the new limit. Start draining | ||
* from the reassembly hashqueue most recently drained. | |||
*/ | */ | ||
if (ip_maxfragpackets < 0) | if (ip_maxfragpackets < 0) | ||
; | ; | ||
else { | else { | ||
while (ip_nfragpackets > ip_maxfragpackets && LIST_FIRST(&ipq)) | int wrapped = 0; | ||
ip_freef(LIST_FIRST(&ipq)); | |||
i = dropscanidx; | |||
while (ip_nfragpackets > ip_maxfragpackets && wrapped == 0) { | |||
while (LIST_FIRST(&ipq[i]) != NULL) | |||
ip_freef(LIST_FIRST(&ipq[i])); | |||
if (++i >= IPREASS_NHASH) { | |||
i = 0; | |||
} | |||
/* | |||
* Dont scan forever even if fragment counters are | |||
* wrong: stop after scanning entire reassembly queue. | |||
*/ | |||
if (i == dropscanidx) | |||
wrapped = 1; | |||
} | |||
dropscanidx = i; | |||
} | } | ||
IPQ_UNLOCK(); | IPQ_UNLOCK(); | ||
#ifdef GATEWAY | #ifdef GATEWAY | ||
|
|
||
* Drain off all datagram fragments. | * Drain off all datagram fragments. | ||
*/ | */ | ||
void | void | ||
ip_drain() | ip_drain(void) | ||
{ | { | ||
/* | /* | ||
|
|
||
if (ipq_lock_try() == 0) | if (ipq_lock_try() == 0) | ||
return; | return; | ||
while (LIST_FIRST(&ipq) != NULL) { | /* | ||
ipstat.ips_fragdropped++; | * Drop half the total fragments now. If more mbufs are needed, | ||
ip_freef(LIST_FIRST(&ipq)); | * we will be called again soon. | ||
} | */ | ||
ip_reass_drophalf(); | |||
IPQ_UNLOCK(); | IPQ_UNLOCK(); | ||
} | } | ||
|
|
||
* 0 if the packet should be processed further. | * 0 if the packet should be processed further. | ||
*/ | */ | ||
int | int | ||
ip_dooptions(m) | ip_dooptions(struct mbuf *m) | ||
struct mbuf *m; | |||
{ | { | ||
struct ip *ip = mtod(m, struct ip *); | struct ip *ip = mtod(m, struct ip *); | ||
u_char *cp, *cp0; | u_char *cp, *cp0; | ||
|
|
||
bcopy((caddr_t)(cp + off), (caddr_t)&ipaddr.sin_addr, | bcopy((caddr_t)(cp + off), (caddr_t)&ipaddr.sin_addr, | ||
sizeof(ipaddr.sin_addr)); | sizeof(ipaddr.sin_addr)); | ||
if (opt == IPOPT_SSRR) | if (opt == IPOPT_SSRR) | ||
ia = ifatoia(ifa_ifwithaddr(sintosa(&ipaddr))); | ia = ifatoia(ifa_ifwithladdr(sintosa(&ipaddr))); | ||
else | else | ||
ia = ip_rtaddr(ipaddr.sin_addr); | ia = ip_rtaddr(ipaddr.sin_addr); | ||
if (ia == 0) { | if (ia == 0) { | ||
|
|
||
* return internet address info of interface to be used to get there. | * return internet address info of interface to be used to get there. | ||
*/ | */ | ||
struct in_ifaddr * | struct in_ifaddr * | ||
ip_rtaddr(dst) | ip_rtaddr(struct in_addr dst) | ||
struct in_addr dst; | |||
{ | { | ||
struct sockaddr_in *sin; | struct sockaddr_in *sin; | ||
|
|
||
* to be picked up later by ip_srcroute if the receiver is interested. | * to be picked up later by ip_srcroute if the receiver is interested. | ||
*/ | */ | ||
void | void | ||
save_rte(option, dst) | save_rte(u_char *option, struct in_addr dst) | ||
u_char *option; | |||
struct in_addr dst; | |||
{ | { | ||
unsigned olen; | unsigned olen; | ||
|
|
||
* The first hop is placed before the options, will be removed later. | * The first hop is placed before the options, will be removed later. | ||
*/ | */ | ||
struct mbuf * | struct mbuf * | ||
ip_srcroute() | ip_srcroute(void) | ||
{ | { | ||
struct in_addr *p, *q; | struct in_addr *p, *q; | ||
struct mbuf *m; | struct mbuf *m; | ||
|
|
||
if (m == 0) | if (m == 0) | ||
return ((struct mbuf *)0); | return ((struct mbuf *)0); | ||
MCLAIM(m, &inetdomain.dom_mowner); | |||
#define OPTSIZ (sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt)) | #define OPTSIZ (sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt)) | ||
/* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */ | /* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */ | ||
|
|
||
* XXX should be deleted; last arg currently ignored. | * XXX should be deleted; last arg currently ignored. | ||
*/ | */ | ||
void | void | ||
ip_stripoptions(m, mopt) | ip_stripoptions(struct mbuf *m, struct mbuf *mopt) | ||
struct mbuf *m; | |||
struct mbuf *mopt; | |||
{ | { | ||
int i; | int i; | ||
struct ip *ip = mtod(m, struct ip *); | struct ip *ip = mtod(m, struct ip *); | ||
|
|
||
* via a source route. | * via a source route. | ||
*/ | */ | ||
void | void | ||
ip_forward(m, srcrt) | ip_forward(struct mbuf *m, int srcrt) | ||
struct mbuf *m; | |||
int srcrt; | |||
{ | { | ||
struct ip *ip = mtod(m, struct ip *); | struct ip *ip = mtod(m, struct ip *); | ||
struct sockaddr_in *sin; | struct sockaddr_in *sin; | ||
|
|
||
struct mbuf *mcopy; | struct mbuf *mcopy; | ||
n_long dest; | n_long dest; | ||
struct ifnet *destifp; | struct ifnet *destifp; | ||
#ifdef IPSEC | #if defined(IPSEC) || defined(FAST_IPSEC) | ||
struct ifnet dummyifp; | struct ifnet dummyifp; | ||
#endif | #endif | ||
/* | /* | ||
* We are now in the output path. | |||
*/ | |||
MCLAIM(m, &ip_tx_mowner); | |||
/* | |||
* Clear any in-bound checksum flags for this packet. | * Clear any in-bound checksum flags for this packet. | ||
*/ | */ | ||
m->m_pkthdr.csum_flags = 0; | m->m_pkthdr.csum_flags = 0; | ||
|
|
||
} | } | ||
} | } | ||
#ifdef IPSEC | |||
/* Don't lookup socket in forwarding case */ | |||
(void)ipsec_setsocket(m, NULL); | |||
#endif | |||
error = ip_output(m, (struct mbuf *)0, &ipforward_rt, | error = ip_output(m, (struct mbuf *)0, &ipforward_rt, | ||
(IP_FORWARDING | (ip_directedbcast ? IP_ALLOWBROADCAST : 0)), 0); | (IP_FORWARDING | (ip_directedbcast ? IP_ALLOWBROADCAST : 0)), | ||
(struct ip_moptions *)NULL, (struct socket *)NULL); | |||
if (error) | if (error) | ||
ipstat.ips_cantforward++; | ipstat.ips_cantforward++; | ||
else { | else { | ||
|
|
||
case EMSGSIZE: | case EMSGSIZE: | ||
type = ICMP_UNREACH; | type = ICMP_UNREACH; | ||
code = ICMP_UNREACH_NEEDFRAG; | code = ICMP_UNREACH_NEEDFRAG; | ||
#ifndef IPSEC | #if !defined(IPSEC) && !defined(FAST_IPSEC) | ||
if (ipforward_rt.ro_rt) | if (ipforward_rt.ro_rt) | ||
destifp = ipforward_rt.ro_rt->rt_ifp; | destifp = ipforward_rt.ro_rt->rt_ifp; | ||
#else | #else | ||
|
|
||
struct route *ro; | struct route *ro; | ||
sp = ipsec4_getpolicybyaddr(mcopy, | sp = ipsec4_getpolicybyaddr(mcopy, | ||
IPSEC_DIR_OUTBOUND, | IPSEC_DIR_OUTBOUND, IP_FORWARDING, | ||
IP_FORWARDING, | &ipsecerror); | ||
&ipsecerror); | |||
if (sp == NULL) | if (sp == NULL) | ||
destifp = ipforward_rt.ro_rt->rt_ifp; | destifp = ipforward_rt.ro_rt->rt_ifp; | ||
else { | else { | ||
/* count IPsec header size */ | /* count IPsec header size */ | ||
ipsechdr = ipsec4_hdrsiz(mcopy, | ipsechdr = ipsec4_hdrsiz(mcopy, | ||
IPSEC_DIR_OUTBOUND, | IPSEC_DIR_OUTBOUND, NULL); | ||
NULL); | |||
/* | /* | ||
* find the correct route for outer IPv4 | * find the correct route for outer IPv4 | ||
|
|
||
} | } | ||
} | } | ||
#ifdef IPSEC | |||
key_freesp(sp); | key_freesp(sp); | ||
#else | |||
KEY_FREESP(&sp); | |||
#endif | |||
} | } | ||
} | } | ||
#endif /*IPSEC*/ | #endif /*IPSEC*/ | ||
|
|
||
} | } | ||
void | void | ||
ip_savecontrol(inp, mp, ip, m) | ip_savecontrol(struct inpcb *inp, struct mbuf **mp, struct ip *ip, | ||
struct inpcb *inp; | struct mbuf *m) | ||
struct mbuf **mp; | |||
struct ip *ip; | |||
struct mbuf *m; | |||
{ | { | ||
if (inp->inp_socket->so_options & SO_TIMESTAMP) { | if (inp->inp_socket->so_options & SO_TIMESTAMP) { | ||
|
|
||
} | } | ||
} | } | ||
int | /* | ||
ip_sysctl(name, namelen, oldp, oldlenp, newp, newlen) | * sysctl helper routine for net.inet.ip.mtudisctimeout. checks the | ||
int *name; | * range of the new value and tweaks timers if it changes. | ||
u_int namelen; | */ | ||
void *oldp; | static int | ||
size_t *oldlenp; | sysctl_net_inet_ip_pmtudto(SYSCTLFN_ARGS) | ||
void *newp; | |||
size_t newlen; | |||
{ | { | ||
extern int subnetsarelocal, hostzeroisbroadcast; | int error, tmp; | ||
struct sysctlnode node; | |||
int error, old; | node = *rnode; | ||
tmp = ip_mtudisc_timeout; | |||
/* All sysctl names at this level are terminal. */ | node.sysctl_data = &tmp; | ||
if (namelen != 1) | error = sysctl_lookup(SYSCTLFN_CALL(&node)); | ||
return (ENOTDIR); | if (error || newp == NULL) | ||
switch (name[0]) { | |||
case IPCTL_FORWARDING: | |||
return (sysctl_int(oldp, oldlenp, newp, newlen, &ipforwarding)); | |||
case IPCTL_SENDREDIRECTS: | |||
return (sysctl_int(oldp, oldlenp, newp, newlen, | |||
&ipsendredirects)); | |||
case IPCTL_DEFTTL: | |||
return (sysctl_int(oldp, oldlenp, newp, newlen, &ip_defttl)); | |||
#ifdef notyet | |||
case IPCTL_DEFMTU: | |||
return (sysctl_int(oldp, oldlenp, newp, newlen, &ip_mtu)); | |||
#endif | |||
case IPCTL_FORWSRCRT: | |||
/* Don't allow this to change in a secure environment. */ | |||
if (securelevel > 0) | |||
return (sysctl_rdint(oldp, oldlenp, newp, | |||
ip_forwsrcrt)); | |||
else | |||
return (sysctl_int(oldp, oldlenp, newp, newlen, | |||
&ip_forwsrcrt)); | |||
case IPCTL_DIRECTEDBCAST: | |||
return (sysctl_int(oldp, oldlenp, newp, newlen, | |||
&ip_directedbcast)); | |||
case IPCTL_ALLOWSRCRT: | |||
return (sysctl_int(oldp, oldlenp, newp, newlen, | |||
&ip_allowsrcrt)); | |||
case IPCTL_SUBNETSARELOCAL: | |||
return (sysctl_int(oldp, oldlenp, newp, newlen, | |||
&subnetsarelocal)); | |||
case IPCTL_MTUDISC: | |||
error = sysctl_int(oldp, oldlenp, newp, newlen, | |||
&ip_mtudisc); | |||
if (ip_mtudisc != 0 && ip_mtudisc_timeout_q == NULL) { | |||
ip_mtudisc_timeout_q = | |||
rt_timer_queue_create(ip_mtudisc_timeout); | |||
} else if (ip_mtudisc == 0 && ip_mtudisc_timeout_q != NULL) { | |||
rt_timer_queue_destroy(ip_mtudisc_timeout_q, TRUE); | |||
ip_mtudisc_timeout_q = NULL; | |||
} | |||
return error; | |||
case IPCTL_ANONPORTMIN: | |||
old = anonportmin; | |||
error = sysctl_int(oldp, oldlenp, newp, newlen, &anonportmin); | |||
if (anonportmin >= anonportmax || anonportmin < 0 | |||
|| anonportmin > 65535 | |||
#ifndef IPNOPRIVPORTS | |||
|| anonportmin < IPPORT_RESERVED | |||
#endif | |||
) { | |||
anonportmin = old; | |||
return (EINVAL); | |||
} | |||
return (error); | |||
case IPCTL_ANONPORTMAX: | |||
old = anonportmax; | |||
error = sysctl_int(oldp, oldlenp, newp, newlen, &anonportmax); | |||
if (anonportmin >= anonportmax || anonportmax < 0 | |||
|| anonportmax > 65535 | |||
#ifndef IPNOPRIVPORTS | |||
|| anonportmax < IPPORT_RESERVED | |||
#endif | |||
) { | |||
anonportmax = old; | |||
return (EINVAL); | |||
} | |||
return (error); | |||
case IPCTL_MTUDISCTIMEOUT: | |||
old = ip_mtudisc_timeout; | |||
error = sysctl_int(oldp, oldlenp, newp, newlen, | |||
&ip_mtudisc_timeout); | |||
if (ip_mtudisc_timeout < 0) { | |||
ip_mtudisc_timeout = old; | |||
return (EINVAL); | |||
} | |||
if (ip_mtudisc_timeout_q != NULL) | |||
rt_timer_queue_change(ip_mtudisc_timeout_q, | |||
ip_mtudisc_timeout); | |||
return (error); | return (error); | ||
if (tmp < 0) | |||
return (EINVAL); | |||
ip_mtudisc_timeout = tmp; | |||
rt_timer_queue_change(ip_mtudisc_timeout_q, ip_mtudisc_timeout); | |||
return (0); | |||
} | |||
#ifdef GATEWAY | #ifdef GATEWAY | ||
case IPCTL_MAXFLOWS: | /* | ||
{ | * sysctl helper routine for net.inet.ip.maxflows. apparently if | ||
int s; | * maxflows is even looked up, we "reap flows". | ||
*/ | |||
static int | |||
sysctl_net_inet_ip_maxflows(SYSCTLFN_ARGS) | |||
{ | |||
int s; | |||
error = sysctl_int(oldp, oldlenp, newp, newlen, | s = sysctl_lookup(SYSCTLFN_CALL(rnode)); | ||
&ip_maxflows); | if (s) | ||
s = splsoftnet(); | return (s); | ||
ipflow_reap(0); | |||
splx(s); | |||
return (error); | |||
} | |||
#endif | |||
case IPCTL_HOSTZEROBROADCAST: | |||
return (sysctl_int(oldp, oldlenp, newp, newlen, | |||
&hostzeroisbroadcast)); | |||
#if NGIF > 0 | |||
case IPCTL_GIF_TTL: | |||
return (sysctl_int(oldp, oldlenp, newp, newlen, | |||
&ip_gif_ttl)); | |||
#endif | |||
#if NGRE > 0 | s = splsoftnet(); | ||
case IPCTL_GRE_TTL: | ipflow_reap(0); | ||
return (sysctl_int(oldp, oldlenp, newp, newlen, | splx(s); | ||
&ip_gre_ttl)); | |||
#endif | |||
#ifndef IPNOPRIVPORTS | return (0); | ||
case IPCTL_LOWPORTMIN: | } | ||
old = lowportmin; | #endif /* GATEWAY */ | ||
error = sysctl_int(oldp, oldlenp, newp, newlen, &lowportmin); | |||
if (lowportmin >= lowportmax | |||
|| lowportmin > IPPORT_RESERVEDMAX | |||
|| lowportmin < IPPORT_RESERVEDMIN | |||
) { | |||
lowportmin = old; | |||
return (EINVAL); | |||
} | |||
return (error); | |||
case IPCTL_LOWPORTMAX: | |||
old = lowportmax; | |||
error = sysctl_int(oldp, oldlenp, newp, newlen, &lowportmax); | |||
if (lowportmin >= lowportmax | |||
|| lowportmax > IPPORT_RESERVEDMAX | |||
|| lowportmax < IPPORT_RESERVEDMIN | |||
) { | |||
lowportmax = old; | |||
return (EINVAL); | |||
} | |||
return (error); | |||
#endif | |||
case IPCTL_MAXFRAGPACKETS: | |||
return (sysctl_int(oldp, oldlenp, newp, newlen, | |||
&ip_maxfragpackets)); | |||
default: | SYSCTL_SETUP(sysctl_net_inet_ip_setup, "sysctl net.inet.ip subtree setup") | ||
return (EOPNOTSUPP); | { | ||
} | extern int subnetsarelocal, hostzeroisbroadcast; | ||
/* NOTREACHED */ | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT, | |||
CTLTYPE_NODE, "net", NULL, | |||
NULL, 0, NULL, 0, | |||
CTL_NET, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT, | |||
CTLTYPE_NODE, "inet", | |||
SYSCTL_DESCR("PF_INET related settings"), | |||
NULL, 0, NULL, 0, | |||
CTL_NET, PF_INET, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT, | |||
CTLTYPE_NODE, "ip", | |||
SYSCTL_DESCR("IPv4 related settings"), | |||
NULL, 0, NULL, 0, | |||
CTL_NET, PF_INET, IPPROTO_IP, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "forwarding", | |||
SYSCTL_DESCR("Enable forwarding of INET datagrams"), | |||
NULL, 0, &ipforwarding, 0, | |||
CTL_NET, PF_INET, IPPROTO_IP, | |||
IPCTL_FORWARDING, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "redirect", | |||
SYSCTL_DESCR("Enable sending of ICMP redirect messages"), | |||
NULL, 0, &ipsendredirects, 0, | |||
CTL_NET, PF_INET, IPPROTO_IP, | |||
IPCTL_SENDREDIRECTS, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "ttl", | |||
SYSCTL_DESCR("Default TTL for an INET datagram"), | |||
NULL, 0, &ip_defttl, 0, | |||
CTL_NET, PF_INET, IPPROTO_IP, | |||
IPCTL_DEFTTL, CTL_EOL); | |||
#ifdef IPCTL_DEFMTU | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT /* |CTLFLAG_READWRITE? */, | |||
CTLTYPE_INT, "mtu", | |||
SYSCTL_DESCR("Default MTA for an INET route"), | |||
NULL, 0, &ip_mtu, 0, | |||
CTL_NET, PF_INET, IPPROTO_IP, | |||
IPCTL_DEFMTU, CTL_EOL); | |||
#endif /* IPCTL_DEFMTU */ | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READONLY1, | |||
CTLTYPE_INT, "forwsrcrt", | |||
SYSCTL_DESCR("Enable forwarding of source-routed " | |||
"datagrams"), | |||
NULL, 0, &ip_forwsrcrt, 0, | |||
CTL_NET, PF_INET, IPPROTO_IP, | |||
IPCTL_FORWSRCRT, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "directed-broadcast", | |||
SYSCTL_DESCR("Enable forwarding of broadcast datagrams"), | |||
NULL, 0, &ip_directedbcast, 0, | |||
CTL_NET, PF_INET, IPPROTO_IP, | |||
IPCTL_DIRECTEDBCAST, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "allowsrcrt", | |||
SYSCTL_DESCR("Accept source-routed datagrams"), | |||
NULL, 0, &ip_allowsrcrt, 0, | |||
CTL_NET, PF_INET, IPPROTO_IP, | |||
IPCTL_ALLOWSRCRT, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "subnetsarelocal", | |||
SYSCTL_DESCR("Whether logical subnets are considered " | |||
"local"), | |||
NULL, 0, &subnetsarelocal, 0, | |||
CTL_NET, PF_INET, IPPROTO_IP, | |||
IPCTL_SUBNETSARELOCAL, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "mtudisc", | |||
SYSCTL_DESCR("Use RFC1191 Path MTU Discovery"), | |||
NULL, 0, &ip_mtudisc, 0, | |||
CTL_NET, PF_INET, IPPROTO_IP, | |||
IPCTL_MTUDISC, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "anonportmin", | |||
SYSCTL_DESCR("Lowest ephemeral port number to assign"), | |||
sysctl_net_inet_ip_ports, 0, &anonportmin, 0, | |||
CTL_NET, PF_INET, IPPROTO_IP, | |||
IPCTL_ANONPORTMIN, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "anonportmax", | |||
SYSCTL_DESCR("Highest ephemeral port number to assign"), | |||
sysctl_net_inet_ip_ports, 0, &anonportmax, 0, | |||
CTL_NET, PF_INET, IPPROTO_IP, | |||
IPCTL_ANONPORTMAX, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "mtudisctimeout", | |||
SYSCTL_DESCR("Lifetime of a Path MTU Discovered route"), | |||
sysctl_net_inet_ip_pmtudto, 0, &ip_mtudisc_timeout, 0, | |||
CTL_NET, PF_INET, IPPROTO_IP, | |||
IPCTL_MTUDISCTIMEOUT, CTL_EOL); | |||
#ifdef GATEWAY | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "maxflows", | |||
SYSCTL_DESCR("Number of flows for fast forwarding"), | |||
sysctl_net_inet_ip_maxflows, 0, &ip_maxflows, 0, | |||
CTL_NET, PF_INET, IPPROTO_IP, | |||
IPCTL_MAXFLOWS, CTL_EOL); | |||
#endif /* GATEWAY */ | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "hostzerobroadcast", | |||
SYSCTL_DESCR("All zeroes address is broadcast address"), | |||
NULL, 0, &hostzeroisbroadcast, 0, | |||
CTL_NET, PF_INET, IPPROTO_IP, | |||
IPCTL_HOSTZEROBROADCAST, CTL_EOL); | |||
#if NGIF > 0 | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "gifttl", | |||
SYSCTL_DESCR("Default TTL for a gif tunnel datagram"), | |||
NULL, 0, &ip_gif_ttl, 0, | |||
CTL_NET, PF_INET, IPPROTO_IP, | |||
IPCTL_GIF_TTL, CTL_EOL); | |||
#endif /* NGIF */ | |||
#ifndef IPNOPRIVPORTS | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "lowportmin", | |||
SYSCTL_DESCR("Lowest privileged ephemeral port number " | |||
"to assign"), | |||
sysctl_net_inet_ip_ports, 0, &lowportmin, 0, | |||
CTL_NET, PF_INET, IPPROTO_IP, | |||
IPCTL_LOWPORTMIN, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "lowportmax", | |||
SYSCTL_DESCR("Highest privileged ephemeral port number " | |||
"to assign"), | |||
sysctl_net_inet_ip_ports, 0, &lowportmax, 0, | |||
CTL_NET, PF_INET, IPPROTO_IP, | |||
IPCTL_LOWPORTMAX, CTL_EOL); | |||
#endif /* IPNOPRIVPORTS */ | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "maxfragpackets", | |||
SYSCTL_DESCR("Maximum number of fragments to retain for " | |||
"possible reassembly"), | |||
NULL, 0, &ip_maxfragpackets, 0, | |||
CTL_NET, PF_INET, IPPROTO_IP, | |||
IPCTL_MAXFRAGPACKETS, CTL_EOL); | |||
#if NGRE > 0 | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "grettl", | |||
SYSCTL_DESCR("Default TTL for a gre tunnel datagram"), | |||
NULL, 0, &ip_gre_ttl, 0, | |||
CTL_NET, PF_INET, IPPROTO_IP, | |||
IPCTL_GRE_TTL, CTL_EOL); | |||
#endif /* NGRE */ | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "checkinterface", | |||
SYSCTL_DESCR("Enable receive side of Strong ES model " | |||
"from RFC1122"), | |||
NULL, 0, &ip_checkinterface, 0, | |||
CTL_NET, PF_INET, IPPROTO_IP, | |||
IPCTL_CHECKINTERFACE, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "random_id", | |||
SYSCTL_DESCR("Assign random ip_id values"), | |||
NULL, 0, &ip_do_randomid, 0, | |||
CTL_NET, PF_INET, IPPROTO_IP, | |||
IPCTL_RANDOMID, CTL_EOL); | |||
sysctl_createv(clog, 0, NULL, NULL, | |||
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | |||
CTLTYPE_INT, "do_loopback_cksum", | |||
SYSCTL_DESCR("Perform IP checksum on loopback"), | |||
NULL, 0, &ip_do_loopback_cksum, 0, | |||
CTL_NET, PF_INET, IPPROTO_IP, | |||
IPCTL_LOOPBACKCKSUM, CTL_EOL); | |||
} | } |