2005-01-07 01:45:51 +00:00
|
|
|
/*-
|
1994-05-24 10:09:53 +00:00
|
|
|
* Copyright (c) 1982, 1986, 1988, 1990, 1993
|
|
|
|
* The Regents of the University of California. All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 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
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
* @(#)ip_output.c 8.3 (Berkeley) 1/21/94
|
1999-08-28 01:08:13 +00:00
|
|
|
* $FreeBSD$
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
|
|
|
|
1998-07-06 05:04:33 +00:00
|
|
|
#include "opt_ipfw.h"
|
1999-12-22 19:13:38 +00:00
|
|
|
#include "opt_ipsec.h"
|
2002-07-31 17:21:01 +00:00
|
|
|
#include "opt_mac.h"
|
2003-04-12 06:11:46 +00:00
|
|
|
#include "opt_mbuf_stress_test.h"
|
1997-11-05 20:17:23 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <sys/param.h>
|
1994-05-25 09:21:21 +00:00
|
|
|
#include <sys/systm.h>
|
1998-12-21 21:36:40 +00:00
|
|
|
#include <sys/kernel.h>
|
2002-07-31 17:21:01 +00:00
|
|
|
#include <sys/mac.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/mbuf.h>
|
|
|
|
#include <sys/protosw.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/socketvar.h>
|
2003-03-25 05:45:05 +00:00
|
|
|
#include <sys/sysctl.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
#include <net/if.h>
|
2004-08-17 22:05:54 +00:00
|
|
|
#include <net/netisr.h>
|
2004-08-27 15:16:24 +00:00
|
|
|
#include <net/pfil.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <net/route.h>
|
|
|
|
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netinet/in_systm.h>
|
|
|
|
#include <netinet/ip.h>
|
|
|
|
#include <netinet/in_pcb.h>
|
|
|
|
#include <netinet/in_var.h>
|
|
|
|
#include <netinet/ip_var.h>
|
|
|
|
|
1996-04-18 15:49:06 +00:00
|
|
|
#include <machine/in_cksum.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-10-12 20:26:33 +00:00
|
|
|
static MALLOC_DEFINE(M_IPMOPTS, "ip_moptions", "internet multicast options");
|
1997-10-11 18:31:40 +00:00
|
|
|
|
1999-12-22 19:13:38 +00:00
|
|
|
#ifdef IPSEC
|
|
|
|
#include <netinet6/ipsec.h>
|
|
|
|
#include <netkey/key.h>
|
|
|
|
#ifdef IPSEC_DEBUG
|
|
|
|
#include <netkey/key_debug.h>
|
|
|
|
#else
|
|
|
|
#define KEYDEBUG(lev,arg)
|
|
|
|
#endif
|
|
|
|
#endif /*IPSEC*/
|
|
|
|
|
2002-10-16 02:25:05 +00:00
|
|
|
#ifdef FAST_IPSEC
|
|
|
|
#include <netipsec/ipsec.h>
|
|
|
|
#include <netipsec/xform.h>
|
|
|
|
#include <netipsec/key.h>
|
|
|
|
#endif /*FAST_IPSEC*/
|
|
|
|
|
Remove (almost all) global variables that were used to hold
packet forwarding state ("annotations") during ip processing.
The code is considerably cleaner now.
The variables removed by this change are:
ip_divert_cookie used by divert sockets
ip_fw_fwd_addr used for transparent ip redirection
last_pkt used by dynamic pipes in dummynet
Removal of the first two has been done by carrying the annotations
into volatile structs prepended to the mbuf chains, and adding
appropriate code to add/remove annotations in the routines which
make use of them, i.e. ip_input(), ip_output(), tcp_input(),
bdg_forward(), ether_demux(), ether_output_frame(), div_output().
On passing, remove a bug in divert handling of fragmented packet.
Now it is the fragment at offset 0 which sets the divert status of
the whole packet, whereas formerly it was the last incoming fragment
to decide.
Removal of last_pkt required a change in the interface of ip_fw_chk()
and dummynet_io(). On passing, use the same mechanism for dummynet
annotations and for divert/forward annotations.
option IPFIREWALL_FORWARD is effectively useless, the code to
implement it is very small and is now in by default to avoid the
obfuscation of conditionally compiled code.
NOTES:
* there is at least one global variable left, sro_fwd, in ip_output().
I am not sure if/how this can be removed.
* I have deliberately avoided gratuitous style changes in this commit
to avoid cluttering the diffs. Minor stule cleanup will likely be
necessary
* this commit only focused on the IP layer. I am sure there is a
number of global variables used in the TCP and maybe UDP stack.
* despite the number of files touched, there are absolutely no API's
or data structures changed by this commit (except the interfaces of
ip_fw_chk() and dummynet_io(), which are internal anyways), so
an MFC is quite safe and unintrusive (and desirable, given the
improved readability of the code).
MFC after: 10 days
2002-06-22 11:51:02 +00:00
|
|
|
#define print_ip(x, a, y) printf("%s %d.%d.%d.%d%s",\
|
|
|
|
x, (ntohl(a.s_addr)>>24)&0xFF,\
|
|
|
|
(ntohl(a.s_addr)>>16)&0xFF,\
|
|
|
|
(ntohl(a.s_addr)>>8)&0xFF,\
|
|
|
|
(ntohl(a.s_addr))&0xFF, y);
|
1998-07-06 03:20:19 +00:00
|
|
|
|
1994-08-18 22:36:09 +00:00
|
|
|
u_short ip_id;
|
|
|
|
|
2003-04-12 06:11:46 +00:00
|
|
|
#ifdef MBUF_STRESS_TEST
|
2003-03-25 05:45:05 +00:00
|
|
|
int mbuf_frag_size = 0;
|
|
|
|
SYSCTL_INT(_net_inet_ip, OID_AUTO, mbuf_frag_size, CTLFLAG_RW,
|
|
|
|
&mbuf_frag_size, 0, "Fragment outgoing mbufs to this size");
|
|
|
|
#endif
|
|
|
|
|
2002-03-19 21:25:46 +00:00
|
|
|
static struct mbuf *ip_insertoptions(struct mbuf *, struct mbuf *, int *);
|
|
|
|
static struct ifnet *ip_multicast_if(struct in_addr *, int *);
|
1995-11-14 20:34:56 +00:00
|
|
|
static void ip_mloopback
|
2002-03-19 21:25:46 +00:00
|
|
|
(struct ifnet *, struct mbuf *, struct sockaddr_in *, int);
|
2004-12-05 22:08:37 +00:00
|
|
|
static int ip_getmoptions(struct inpcb *, struct sockopt *);
|
2004-12-05 19:11:09 +00:00
|
|
|
static int ip_pcbopts(struct inpcb *, int, struct mbuf *);
|
2004-12-05 21:38:33 +00:00
|
|
|
static int ip_setmoptions(struct inpcb *, struct sockopt *);
|
2005-08-09 17:19:21 +00:00
|
|
|
static struct ip_moptions *ip_findmoptions(struct inpcb *inp);
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2002-03-19 21:25:46 +00:00
|
|
|
int ip_optcopy(struct ip *, struct ip *);
|
1997-02-10 11:45:37 +00:00
|
|
|
|
|
|
|
|
1996-07-10 19:44:30 +00:00
|
|
|
extern struct protosw inetsw[];
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* IP output. The packet in mbuf chain m contains a skeletal IP
|
|
|
|
* header (with len, off, ttl, proto, tos, src, dst).
|
|
|
|
* The mbuf chain containing the packet will be freed.
|
|
|
|
* The mbuf opt, if present, will not be freed.
|
2003-11-03 18:03:05 +00:00
|
|
|
* In the IP forwarding case, the packet will arrive with options already
|
|
|
|
* inserted, so must have a NULL opt pointer.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
|
|
|
int
|
2004-02-25 19:55:29 +00:00
|
|
|
ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro,
|
2003-08-07 18:16:59 +00:00
|
|
|
int flags, struct ip_moptions *imo, struct inpcb *inp)
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2003-08-07 18:16:59 +00:00
|
|
|
struct ip *ip;
|
Remove (almost all) global variables that were used to hold
packet forwarding state ("annotations") during ip processing.
The code is considerably cleaner now.
The variables removed by this change are:
ip_divert_cookie used by divert sockets
ip_fw_fwd_addr used for transparent ip redirection
last_pkt used by dynamic pipes in dummynet
Removal of the first two has been done by carrying the annotations
into volatile structs prepended to the mbuf chains, and adding
appropriate code to add/remove annotations in the routines which
make use of them, i.e. ip_input(), ip_output(), tcp_input(),
bdg_forward(), ether_demux(), ether_output_frame(), div_output().
On passing, remove a bug in divert handling of fragmented packet.
Now it is the fragment at offset 0 which sets the divert status of
the whole packet, whereas formerly it was the last incoming fragment
to decide.
Removal of last_pkt required a change in the interface of ip_fw_chk()
and dummynet_io(). On passing, use the same mechanism for dummynet
annotations and for divert/forward annotations.
option IPFIREWALL_FORWARD is effectively useless, the code to
implement it is very small and is now in by default to avoid the
obfuscation of conditionally compiled code.
NOTES:
* there is at least one global variable left, sro_fwd, in ip_output().
I am not sure if/how this can be removed.
* I have deliberately avoided gratuitous style changes in this commit
to avoid cluttering the diffs. Minor stule cleanup will likely be
necessary
* this commit only focused on the IP layer. I am sure there is a
number of global variables used in the TCP and maybe UDP stack.
* despite the number of files touched, there are absolutely no API's
or data structures changed by this commit (except the interfaces of
ip_fw_chk() and dummynet_io(), which are internal anyways), so
an MFC is quite safe and unintrusive (and desirable, given the
improved readability of the code).
MFC after: 10 days
2002-06-22 11:51:02 +00:00
|
|
|
struct ifnet *ifp = NULL; /* keep compiler happy */
|
2004-02-25 19:55:29 +00:00
|
|
|
struct mbuf *m0;
|
1996-04-03 13:52:20 +00:00
|
|
|
int hlen = sizeof (struct ip);
|
2004-08-17 22:05:54 +00:00
|
|
|
int len, error = 0;
|
Remove (almost all) global variables that were used to hold
packet forwarding state ("annotations") during ip processing.
The code is considerably cleaner now.
The variables removed by this change are:
ip_divert_cookie used by divert sockets
ip_fw_fwd_addr used for transparent ip redirection
last_pkt used by dynamic pipes in dummynet
Removal of the first two has been done by carrying the annotations
into volatile structs prepended to the mbuf chains, and adding
appropriate code to add/remove annotations in the routines which
make use of them, i.e. ip_input(), ip_output(), tcp_input(),
bdg_forward(), ether_demux(), ether_output_frame(), div_output().
On passing, remove a bug in divert handling of fragmented packet.
Now it is the fragment at offset 0 which sets the divert status of
the whole packet, whereas formerly it was the last incoming fragment
to decide.
Removal of last_pkt required a change in the interface of ip_fw_chk()
and dummynet_io(). On passing, use the same mechanism for dummynet
annotations and for divert/forward annotations.
option IPFIREWALL_FORWARD is effectively useless, the code to
implement it is very small and is now in by default to avoid the
obfuscation of conditionally compiled code.
NOTES:
* there is at least one global variable left, sro_fwd, in ip_output().
I am not sure if/how this can be removed.
* I have deliberately avoided gratuitous style changes in this commit
to avoid cluttering the diffs. Minor stule cleanup will likely be
necessary
* this commit only focused on the IP layer. I am sure there is a
number of global variables used in the TCP and maybe UDP stack.
* despite the number of files touched, there are absolutely no API's
or data structures changed by this commit (except the interfaces of
ip_fw_chk() and dummynet_io(), which are internal anyways), so
an MFC is quite safe and unintrusive (and desirable, given the
improved readability of the code).
MFC after: 10 days
2002-06-22 11:51:02 +00:00
|
|
|
struct sockaddr_in *dst = NULL; /* keep compiler happy */
|
2002-07-12 22:08:47 +00:00
|
|
|
struct in_ifaddr *ia = NULL;
|
2000-03-27 19:14:27 +00:00
|
|
|
int isbroadcast, sw_csum;
|
2002-03-22 16:45:54 +00:00
|
|
|
struct route iproute;
|
2004-08-17 22:05:54 +00:00
|
|
|
struct in_addr odst;
|
|
|
|
#ifdef IPFIREWALL_FORWARD
|
|
|
|
struct m_tag *fwd_tag = NULL;
|
|
|
|
#endif
|
2003-11-14 21:48:57 +00:00
|
|
|
#ifdef IPSEC
|
1999-12-22 19:13:38 +00:00
|
|
|
struct secpolicy *sp = NULL;
|
|
|
|
#endif
|
2002-10-16 02:25:05 +00:00
|
|
|
#ifdef FAST_IPSEC
|
|
|
|
struct secpolicy *sp = NULL;
|
|
|
|
struct tdb_ident *tdbi;
|
2004-08-17 22:05:54 +00:00
|
|
|
struct m_tag *mtag;
|
2002-10-16 02:25:05 +00:00
|
|
|
int s;
|
|
|
|
#endif /* FAST_IPSEC */
|
Remove (almost all) global variables that were used to hold
packet forwarding state ("annotations") during ip processing.
The code is considerably cleaner now.
The variables removed by this change are:
ip_divert_cookie used by divert sockets
ip_fw_fwd_addr used for transparent ip redirection
last_pkt used by dynamic pipes in dummynet
Removal of the first two has been done by carrying the annotations
into volatile structs prepended to the mbuf chains, and adding
appropriate code to add/remove annotations in the routines which
make use of them, i.e. ip_input(), ip_output(), tcp_input(),
bdg_forward(), ether_demux(), ether_output_frame(), div_output().
On passing, remove a bug in divert handling of fragmented packet.
Now it is the fragment at offset 0 which sets the divert status of
the whole packet, whereas formerly it was the last incoming fragment
to decide.
Removal of last_pkt required a change in the interface of ip_fw_chk()
and dummynet_io(). On passing, use the same mechanism for dummynet
annotations and for divert/forward annotations.
option IPFIREWALL_FORWARD is effectively useless, the code to
implement it is very small and is now in by default to avoid the
obfuscation of conditionally compiled code.
NOTES:
* there is at least one global variable left, sro_fwd, in ip_output().
I am not sure if/how this can be removed.
* I have deliberately avoided gratuitous style changes in this commit
to avoid cluttering the diffs. Minor stule cleanup will likely be
necessary
* this commit only focused on the IP layer. I am sure there is a
number of global variables used in the TCP and maybe UDP stack.
* despite the number of files touched, there are absolutely no API's
or data structures changed by this commit (except the interfaces of
ip_fw_chk() and dummynet_io(), which are internal anyways), so
an MFC is quite safe and unintrusive (and desirable, given the
improved readability of the code).
MFC after: 10 days
2002-06-22 11:51:02 +00:00
|
|
|
|
2003-04-08 14:25:47 +00:00
|
|
|
M_ASSERTPKTHDR(m);
|
2004-02-25 19:55:29 +00:00
|
|
|
|
2003-11-14 21:48:57 +00:00
|
|
|
if (ro == NULL) {
|
|
|
|
ro = &iproute;
|
|
|
|
bzero(ro, sizeof (*ro));
|
|
|
|
}
|
|
|
|
|
2003-11-08 23:03:29 +00:00
|
|
|
if (inp != NULL)
|
|
|
|
INP_LOCK_ASSERT(inp);
|
2002-05-21 18:52:24 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
if (opt) {
|
2002-09-23 08:56:24 +00:00
|
|
|
len = 0;
|
1994-05-24 10:09:53 +00:00
|
|
|
m = ip_insertoptions(m, opt, &len);
|
2002-09-23 08:56:24 +00:00
|
|
|
if (len != 0)
|
2002-09-17 11:13:04 +00:00
|
|
|
hlen = len;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
ip = mtod(m, struct ip *);
|
2001-12-28 21:21:57 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
2003-05-31 17:55:21 +00:00
|
|
|
* Fill in IP header. If we are not allowing fragmentation,
|
2004-01-08 11:13:40 +00:00
|
|
|
* then the ip_id field is meaningless, but we don't set it
|
|
|
|
* to zero. Doing so causes various problems when devices along
|
|
|
|
* the path (routers, load balancers, firewalls, etc.) illegally
|
|
|
|
* disable DF on our packet. Note that a 16-bit counter
|
2003-05-31 17:55:21 +00:00
|
|
|
* will wrap around in less than 10 seconds at 100 Mbit/s on a
|
|
|
|
* medium with MTU 1500. See Steven M. Bellovin, "A Technique
|
|
|
|
* for Counting NATted Hosts", Proc. IMW'02, available at
|
|
|
|
* <http://www.research.att.com/~smb/papers/fnat.pdf>.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
|
|
|
if ((flags & (IP_FORWARDING|IP_RAWOUTPUT)) == 0) {
|
2002-10-20 22:52:07 +00:00
|
|
|
ip->ip_v = IPVERSION;
|
|
|
|
ip->ip_hl = hlen >> 2;
|
2004-08-14 15:32:40 +00:00
|
|
|
ip->ip_id = ip_newid();
|
1994-05-24 10:09:53 +00:00
|
|
|
ipstat.ips_localout++;
|
|
|
|
} else {
|
2002-10-20 22:52:07 +00:00
|
|
|
hlen = ip->ip_hl << 2;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
1996-04-18 15:49:06 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
dst = (struct sockaddr_in *)&ro->ro_dst;
|
2004-08-17 22:05:54 +00:00
|
|
|
again:
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* If there is a cached route,
|
|
|
|
* check that it is to the same destination
|
|
|
|
* and is still up. If not, free it and try again.
|
2002-01-21 20:04:22 +00:00
|
|
|
* The address family should also be checked in case of sharing the
|
|
|
|
* cache with IPv6.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
|
|
|
if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
|
2002-01-21 20:04:22 +00:00
|
|
|
dst->sin_family != AF_INET ||
|
2004-08-17 22:05:54 +00:00
|
|
|
dst->sin_addr.s_addr != ip->ip_dst.s_addr)) {
|
1994-05-24 10:09:53 +00:00
|
|
|
RTFREE(ro->ro_rt);
|
|
|
|
ro->ro_rt = (struct rtentry *)0;
|
|
|
|
}
|
2004-08-17 22:05:54 +00:00
|
|
|
#ifdef IPFIREWALL_FORWARD
|
|
|
|
if (ro->ro_rt == NULL && fwd_tag == NULL) {
|
|
|
|
#else
|
2004-08-11 10:46:15 +00:00
|
|
|
if (ro->ro_rt == NULL) {
|
2004-08-17 22:05:54 +00:00
|
|
|
#endif
|
2002-01-21 20:04:22 +00:00
|
|
|
bzero(dst, sizeof(*dst));
|
1994-05-24 10:09:53 +00:00
|
|
|
dst->sin_family = AF_INET;
|
|
|
|
dst->sin_len = sizeof(*dst);
|
2004-08-17 22:05:54 +00:00
|
|
|
dst->sin_addr = ip->ip_dst;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* If routing to interface only,
|
|
|
|
* short circuit routing lookup.
|
|
|
|
*/
|
|
|
|
if (flags & IP_ROUTETOIF) {
|
2004-08-11 10:46:15 +00:00
|
|
|
if ((ia = ifatoia(ifa_ifwithdstaddr(sintosa(dst)))) == NULL &&
|
|
|
|
(ia = ifatoia(ifa_ifwithnet(sintosa(dst)))) == NULL) {
|
1994-05-24 10:09:53 +00:00
|
|
|
ipstat.ips_noroute++;
|
|
|
|
error = ENETUNREACH;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
ifp = ia->ia_ifp;
|
|
|
|
ip->ip_ttl = 1;
|
1996-05-06 17:42:13 +00:00
|
|
|
isbroadcast = in_broadcast(dst->sin_addr, ifp);
|
2001-07-17 18:47:48 +00:00
|
|
|
} else if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) &&
|
2001-07-23 16:50:01 +00:00
|
|
|
imo != NULL && imo->imo_multicast_ifp != NULL) {
|
2001-07-17 18:47:48 +00:00
|
|
|
/*
|
2001-07-23 16:50:01 +00:00
|
|
|
* Bypass the normal routing lookup for multicast
|
|
|
|
* packets if the interface is specified.
|
2001-07-17 18:47:48 +00:00
|
|
|
*/
|
2001-07-23 16:50:01 +00:00
|
|
|
ifp = imo->imo_multicast_ifp;
|
|
|
|
IFP_TO_IA(ifp, ia);
|
|
|
|
isbroadcast = 0; /* fool gcc */
|
1994-05-24 10:09:53 +00:00
|
|
|
} else {
|
1994-12-13 23:08:12 +00:00
|
|
|
/*
|
2003-11-20 20:07:39 +00:00
|
|
|
* We want to do any cloning requested by the link layer,
|
|
|
|
* as this is probably required in all cases for correct
|
|
|
|
* operation (as it is for ARP).
|
1994-12-13 23:08:12 +00:00
|
|
|
*/
|
2004-08-11 10:46:15 +00:00
|
|
|
if (ro->ro_rt == NULL)
|
2004-04-14 01:13:14 +00:00
|
|
|
rtalloc_ign(ro, 0);
|
2004-08-11 10:46:15 +00:00
|
|
|
if (ro->ro_rt == NULL) {
|
1994-05-24 10:09:53 +00:00
|
|
|
ipstat.ips_noroute++;
|
|
|
|
error = EHOSTUNREACH;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
ia = ifatoia(ro->ro_rt->rt_ifa);
|
|
|
|
ifp = ro->ro_rt->rt_ifp;
|
2003-11-20 20:07:39 +00:00
|
|
|
ro->ro_rt->rt_rmx.rmx_pksent++;
|
1994-05-24 10:09:53 +00:00
|
|
|
if (ro->ro_rt->rt_flags & RTF_GATEWAY)
|
2001-07-19 07:10:30 +00:00
|
|
|
dst = (struct sockaddr_in *)ro->ro_rt->rt_gateway;
|
1996-05-06 17:42:13 +00:00
|
|
|
if (ro->ro_rt->rt_flags & RTF_HOST)
|
2001-07-19 07:10:30 +00:00
|
|
|
isbroadcast = (ro->ro_rt->rt_flags & RTF_BROADCAST);
|
1996-05-06 17:42:13 +00:00
|
|
|
else
|
|
|
|
isbroadcast = in_broadcast(dst->sin_addr, ifp);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
2004-08-17 22:05:54 +00:00
|
|
|
if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
|
1994-05-24 10:09:53 +00:00
|
|
|
struct in_multi *inm;
|
|
|
|
|
|
|
|
m->m_flags |= M_MCAST;
|
|
|
|
/*
|
|
|
|
* IP destination address is multicast. Make sure "dst"
|
|
|
|
* still points to the address in "ro". (It may have been
|
|
|
|
* changed to point to a gateway address, above.)
|
|
|
|
*/
|
|
|
|
dst = (struct sockaddr_in *)&ro->ro_dst;
|
|
|
|
/*
|
|
|
|
* See if the caller provided any multicast options
|
|
|
|
*/
|
|
|
|
if (imo != NULL) {
|
|
|
|
ip->ip_ttl = imo->imo_multicast_ttl;
|
1995-06-13 17:51:16 +00:00
|
|
|
if (imo->imo_multicast_vif != -1)
|
|
|
|
ip->ip_src.s_addr =
|
Massive cleanup of the ip_mroute code.
No functional changes, but:
+ the mrouting module now should behave the same as the compiled-in
version (it did not before, some of the rsvp code was not loaded
properly);
+ netinet/ip_mroute.c is now truly optional;
+ removed some redundant/unused code;
+ changed many instances of '0' to NULL and INADDR_ANY as appropriate;
+ removed several static variables to make the code more SMP-friendly;
+ fixed some minor bugs in the mrouting code (mostly, incorrect return
values from functions).
This commit is also a prerequisite to the addition of support for PIM,
which i would like to put in before DP2 (it does not change any of
the existing APIs, anyways).
Note, in the process we found out that some device drivers fail to
properly handle changes in IFF_ALLMULTI, leading to interesting
behaviour when a multicast router is started. This bug is not
corrected by this commit, and will be fixed with a separate commit.
Detailed changes:
--------------------
netinet/ip_mroute.c all the above.
conf/files make ip_mroute.c optional
net/route.c fix mrt_ioctl hook
netinet/ip_input.c fix ip_mforward hook, move rsvp_input() here
together with other rsvp code, and a couple
of indentation fixes.
netinet/ip_output.c fix ip_mforward and ip_mcast_src hooks
netinet/ip_var.h rsvp function hooks
netinet/raw_ip.c hooks for mrouting and rsvp functions, plus
interface cleanup.
netinet/ip_mroute.h remove an unused and optional field from a struct
Most of the code is from Pavlin Radoslavov and the XORP project
Reviewed by: sam
MFC after: 1 week
2002-11-15 22:53:53 +00:00
|
|
|
ip_mcast_src ?
|
|
|
|
ip_mcast_src(imo->imo_multicast_vif) :
|
|
|
|
INADDR_ANY;
|
1994-05-24 10:09:53 +00:00
|
|
|
} else
|
|
|
|
ip->ip_ttl = IP_DEFAULT_MULTICAST_TTL;
|
|
|
|
/*
|
|
|
|
* Confirm that the outgoing interface supports multicast.
|
|
|
|
*/
|
1995-06-13 17:51:16 +00:00
|
|
|
if ((imo == NULL) || (imo->imo_multicast_vif == -1)) {
|
|
|
|
if ((ifp->if_flags & IFF_MULTICAST) == 0) {
|
|
|
|
ipstat.ips_noroute++;
|
|
|
|
error = ENETUNREACH;
|
|
|
|
goto bad;
|
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* If source address not specified yet, use address
|
|
|
|
* of outgoing interface.
|
|
|
|
*/
|
|
|
|
if (ip->ip_src.s_addr == INADDR_ANY) {
|
2001-07-23 16:50:01 +00:00
|
|
|
/* Interface may have no addresses. */
|
|
|
|
if (ia != NULL)
|
|
|
|
ip->ip_src = IA_SIN(ia)->sin_addr;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
Introduce in_multi_mtx, which will protect IPv4-layer multicast address
lists, as well as accessor macros. For now, this is a recursive mutex
due code sequences where IPv4 multicast calls into IGMP calls into
ip_output(), which then tests for a multicast forwarding case.
For support macros in in_var.h to check multicast address lists, assert
that in_multi_mtx is held.
Acquire in_multi_mtx around iteration over the IPv4 multicast address
lists, such as in ip_input() and ip_output().
Acquire in_multi_mtx when manipulating the IPv4 layer multicast addresses,
as well as over the manipulation of ifnet multicast address lists in order
to keep the two layers in sync.
Lock down accesses to IPv4 multicast addresses in IGMP, or assert the
lock when performing IGMP join/leave events.
Eliminate spl's associated with IPv4 multicast addresses, portions of
IGMP that weren't previously expunged by IGMP locking.
Add in_multi_mtx, igmp_mtx, and if_addr_mtx lock order to hard-coded
lock order in WITNESS, in that order.
Problem reported by: Ed Maste <emaste at phaedrus dot sandvine dot ca>
MFC after: 10 days
2005-08-03 19:29:47 +00:00
|
|
|
IN_MULTI_LOCK();
|
2004-08-17 22:05:54 +00:00
|
|
|
IN_LOOKUP_MULTI(ip->ip_dst, ifp, inm);
|
1994-05-24 10:09:53 +00:00
|
|
|
if (inm != NULL &&
|
|
|
|
(imo == NULL || imo->imo_multicast_loop)) {
|
Introduce in_multi_mtx, which will protect IPv4-layer multicast address
lists, as well as accessor macros. For now, this is a recursive mutex
due code sequences where IPv4 multicast calls into IGMP calls into
ip_output(), which then tests for a multicast forwarding case.
For support macros in in_var.h to check multicast address lists, assert
that in_multi_mtx is held.
Acquire in_multi_mtx around iteration over the IPv4 multicast address
lists, such as in ip_input() and ip_output().
Acquire in_multi_mtx when manipulating the IPv4 layer multicast addresses,
as well as over the manipulation of ifnet multicast address lists in order
to keep the two layers in sync.
Lock down accesses to IPv4 multicast addresses in IGMP, or assert the
lock when performing IGMP join/leave events.
Eliminate spl's associated with IPv4 multicast addresses, portions of
IGMP that weren't previously expunged by IGMP locking.
Add in_multi_mtx, igmp_mtx, and if_addr_mtx lock order to hard-coded
lock order in WITNESS, in that order.
Problem reported by: Ed Maste <emaste at phaedrus dot sandvine dot ca>
MFC after: 10 days
2005-08-03 19:29:47 +00:00
|
|
|
IN_MULTI_UNLOCK();
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* If we belong to the destination multicast group
|
|
|
|
* on the outgoing interface, and the caller did not
|
|
|
|
* forbid loopback, loop back a copy.
|
|
|
|
*/
|
1997-05-06 21:22:04 +00:00
|
|
|
ip_mloopback(ifp, m, dst, hlen);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
else {
|
Introduce in_multi_mtx, which will protect IPv4-layer multicast address
lists, as well as accessor macros. For now, this is a recursive mutex
due code sequences where IPv4 multicast calls into IGMP calls into
ip_output(), which then tests for a multicast forwarding case.
For support macros in in_var.h to check multicast address lists, assert
that in_multi_mtx is held.
Acquire in_multi_mtx around iteration over the IPv4 multicast address
lists, such as in ip_input() and ip_output().
Acquire in_multi_mtx when manipulating the IPv4 layer multicast addresses,
as well as over the manipulation of ifnet multicast address lists in order
to keep the two layers in sync.
Lock down accesses to IPv4 multicast addresses in IGMP, or assert the
lock when performing IGMP join/leave events.
Eliminate spl's associated with IPv4 multicast addresses, portions of
IGMP that weren't previously expunged by IGMP locking.
Add in_multi_mtx, igmp_mtx, and if_addr_mtx lock order to hard-coded
lock order in WITNESS, in that order.
Problem reported by: Ed Maste <emaste at phaedrus dot sandvine dot ca>
MFC after: 10 days
2005-08-03 19:29:47 +00:00
|
|
|
IN_MULTI_UNLOCK();
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* If we are acting as a multicast router, perform
|
|
|
|
* multicast forwarding as if the packet had just
|
|
|
|
* arrived on the interface to which we are about
|
|
|
|
* to send. The multicast forwarding function
|
|
|
|
* recursively calls this function, using the
|
|
|
|
* IP_FORWARDING flag to prevent infinite recursion.
|
|
|
|
*
|
|
|
|
* Multicasts that are looped back by ip_mloopback(),
|
|
|
|
* above, will be forwarded by the ip_input() routine,
|
|
|
|
* if necessary.
|
|
|
|
*/
|
|
|
|
if (ip_mrouter && (flags & IP_FORWARDING) == 0) {
|
1994-09-06 22:42:31 +00:00
|
|
|
/*
|
Massive cleanup of the ip_mroute code.
No functional changes, but:
+ the mrouting module now should behave the same as the compiled-in
version (it did not before, some of the rsvp code was not loaded
properly);
+ netinet/ip_mroute.c is now truly optional;
+ removed some redundant/unused code;
+ changed many instances of '0' to NULL and INADDR_ANY as appropriate;
+ removed several static variables to make the code more SMP-friendly;
+ fixed some minor bugs in the mrouting code (mostly, incorrect return
values from functions).
This commit is also a prerequisite to the addition of support for PIM,
which i would like to put in before DP2 (it does not change any of
the existing APIs, anyways).
Note, in the process we found out that some device drivers fail to
properly handle changes in IFF_ALLMULTI, leading to interesting
behaviour when a multicast router is started. This bug is not
corrected by this commit, and will be fixed with a separate commit.
Detailed changes:
--------------------
netinet/ip_mroute.c all the above.
conf/files make ip_mroute.c optional
net/route.c fix mrt_ioctl hook
netinet/ip_input.c fix ip_mforward hook, move rsvp_input() here
together with other rsvp code, and a couple
of indentation fixes.
netinet/ip_output.c fix ip_mforward and ip_mcast_src hooks
netinet/ip_var.h rsvp function hooks
netinet/raw_ip.c hooks for mrouting and rsvp functions, plus
interface cleanup.
netinet/ip_mroute.h remove an unused and optional field from a struct
Most of the code is from Pavlin Radoslavov and the XORP project
Reviewed by: sam
MFC after: 1 week
2002-11-15 22:53:53 +00:00
|
|
|
* If rsvp daemon is not running, do not
|
1994-09-06 22:42:31 +00:00
|
|
|
* set ip_moptions. This ensures that the packet
|
|
|
|
* is multicast and not just sent down one link
|
|
|
|
* as prescribed by rsvpd.
|
|
|
|
*/
|
1995-07-26 18:05:16 +00:00
|
|
|
if (!rsvp_on)
|
Massive cleanup of the ip_mroute code.
No functional changes, but:
+ the mrouting module now should behave the same as the compiled-in
version (it did not before, some of the rsvp code was not loaded
properly);
+ netinet/ip_mroute.c is now truly optional;
+ removed some redundant/unused code;
+ changed many instances of '0' to NULL and INADDR_ANY as appropriate;
+ removed several static variables to make the code more SMP-friendly;
+ fixed some minor bugs in the mrouting code (mostly, incorrect return
values from functions).
This commit is also a prerequisite to the addition of support for PIM,
which i would like to put in before DP2 (it does not change any of
the existing APIs, anyways).
Note, in the process we found out that some device drivers fail to
properly handle changes in IFF_ALLMULTI, leading to interesting
behaviour when a multicast router is started. This bug is not
corrected by this commit, and will be fixed with a separate commit.
Detailed changes:
--------------------
netinet/ip_mroute.c all the above.
conf/files make ip_mroute.c optional
net/route.c fix mrt_ioctl hook
netinet/ip_input.c fix ip_mforward hook, move rsvp_input() here
together with other rsvp code, and a couple
of indentation fixes.
netinet/ip_output.c fix ip_mforward and ip_mcast_src hooks
netinet/ip_var.h rsvp function hooks
netinet/raw_ip.c hooks for mrouting and rsvp functions, plus
interface cleanup.
netinet/ip_mroute.h remove an unused and optional field from a struct
Most of the code is from Pavlin Radoslavov and the XORP project
Reviewed by: sam
MFC after: 1 week
2002-11-15 22:53:53 +00:00
|
|
|
imo = NULL;
|
|
|
|
if (ip_mforward &&
|
|
|
|
ip_mforward(ip, ifp, m, imo) != 0) {
|
1994-05-24 10:09:53 +00:00
|
|
|
m_freem(m);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1994-09-14 03:10:15 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* Multicasts with a time-to-live of zero may be looped-
|
|
|
|
* back, above, but must not be transmitted on a network.
|
|
|
|
* Also, multicasts addressed to the loopback interface
|
|
|
|
* are not sent -- the above call to ip_mloopback() will
|
|
|
|
* loop back a copy if this host actually belongs to the
|
|
|
|
* destination group on the loopback interface.
|
|
|
|
*/
|
1995-04-26 18:10:58 +00:00
|
|
|
if (ip->ip_ttl == 0 || ifp->if_flags & IFF_LOOPBACK) {
|
1994-05-24 10:09:53 +00:00
|
|
|
m_freem(m);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
goto sendit;
|
|
|
|
}
|
|
|
|
#ifndef notdef
|
|
|
|
/*
|
Remove (almost all) global variables that were used to hold
packet forwarding state ("annotations") during ip processing.
The code is considerably cleaner now.
The variables removed by this change are:
ip_divert_cookie used by divert sockets
ip_fw_fwd_addr used for transparent ip redirection
last_pkt used by dynamic pipes in dummynet
Removal of the first two has been done by carrying the annotations
into volatile structs prepended to the mbuf chains, and adding
appropriate code to add/remove annotations in the routines which
make use of them, i.e. ip_input(), ip_output(), tcp_input(),
bdg_forward(), ether_demux(), ether_output_frame(), div_output().
On passing, remove a bug in divert handling of fragmented packet.
Now it is the fragment at offset 0 which sets the divert status of
the whole packet, whereas formerly it was the last incoming fragment
to decide.
Removal of last_pkt required a change in the interface of ip_fw_chk()
and dummynet_io(). On passing, use the same mechanism for dummynet
annotations and for divert/forward annotations.
option IPFIREWALL_FORWARD is effectively useless, the code to
implement it is very small and is now in by default to avoid the
obfuscation of conditionally compiled code.
NOTES:
* there is at least one global variable left, sro_fwd, in ip_output().
I am not sure if/how this can be removed.
* I have deliberately avoided gratuitous style changes in this commit
to avoid cluttering the diffs. Minor stule cleanup will likely be
necessary
* this commit only focused on the IP layer. I am sure there is a
number of global variables used in the TCP and maybe UDP stack.
* despite the number of files touched, there are absolutely no API's
or data structures changed by this commit (except the interfaces of
ip_fw_chk() and dummynet_io(), which are internal anyways), so
an MFC is quite safe and unintrusive (and desirable, given the
improved readability of the code).
MFC after: 10 days
2002-06-22 11:51:02 +00:00
|
|
|
* If the source address is not specified yet, use the address
|
2004-09-06 15:48:38 +00:00
|
|
|
* of the outoing interface.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
1998-07-06 03:20:19 +00:00
|
|
|
if (ip->ip_src.s_addr == INADDR_ANY) {
|
2001-07-23 16:50:01 +00:00
|
|
|
/* Interface may have no addresses. */
|
|
|
|
if (ia != NULL) {
|
|
|
|
ip->ip_src = IA_SIN(ia)->sin_addr;
|
|
|
|
}
|
1998-07-06 03:20:19 +00:00
|
|
|
}
|
|
|
|
#endif /* notdef */
|
1994-08-01 12:01:45 +00:00
|
|
|
/*
|
2004-08-22 16:42:28 +00:00
|
|
|
* Verify that we have any chance at all of being able to queue the
|
|
|
|
* packet or packet fragments, unless ALTQ is enabled on the given
|
|
|
|
* interface in which case packetdrop should be done by queueing.
|
1994-08-01 12:01:45 +00:00
|
|
|
*/
|
2004-08-22 16:42:28 +00:00
|
|
|
#ifdef ALTQ
|
|
|
|
if ((!ALTQ_IS_ENABLED(&ifp->if_snd)) &&
|
|
|
|
((ifp->if_snd.ifq_len + ip->ip_len / ifp->if_mtu + 1) >=
|
|
|
|
ifp->if_snd.ifq_maxlen))
|
|
|
|
#else
|
1994-08-01 12:01:45 +00:00
|
|
|
if ((ifp->if_snd.ifq_len + ip->ip_len / ifp->if_mtu + 1) >=
|
2004-08-22 16:42:28 +00:00
|
|
|
ifp->if_snd.ifq_maxlen)
|
|
|
|
#endif /* ALTQ */
|
|
|
|
{
|
|
|
|
error = ENOBUFS;
|
|
|
|
ipstat.ips_odropped++;
|
|
|
|
goto bad;
|
1994-08-01 12:01:45 +00:00
|
|
|
}
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* Look for broadcast address and
|
2002-01-21 13:59:42 +00:00
|
|
|
* verify user is allowed to send
|
1994-05-24 10:09:53 +00:00
|
|
|
* such a packet.
|
|
|
|
*/
|
1996-05-06 17:42:13 +00:00
|
|
|
if (isbroadcast) {
|
1994-05-24 10:09:53 +00:00
|
|
|
if ((ifp->if_flags & IFF_BROADCAST) == 0) {
|
|
|
|
error = EADDRNOTAVAIL;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
if ((flags & IP_ALLOWBROADCAST) == 0) {
|
|
|
|
error = EACCES;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
/* don't allow broadcast messages to be fragmented */
|
2003-08-07 18:16:59 +00:00
|
|
|
if (ip->ip_len > ifp->if_mtu) {
|
1994-05-24 10:09:53 +00:00
|
|
|
error = EMSGSIZE;
|
|
|
|
goto bad;
|
|
|
|
}
|
2003-08-20 14:46:40 +00:00
|
|
|
if (flags & IP_SENDONES)
|
|
|
|
ip->ip_dst.s_addr = INADDR_BROADCAST;
|
1994-05-24 10:09:53 +00:00
|
|
|
m->m_flags |= M_BCAST;
|
1996-05-06 17:42:13 +00:00
|
|
|
} else {
|
1994-05-24 10:09:53 +00:00
|
|
|
m->m_flags &= ~M_BCAST;
|
1996-05-06 17:42:13 +00:00
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-02-19 14:02:27 +00:00
|
|
|
sendit:
|
2001-06-11 12:39:29 +00:00
|
|
|
#ifdef IPSEC
|
|
|
|
/* get SP for this packet */
|
2004-02-03 18:20:55 +00:00
|
|
|
if (inp == NULL)
|
- cleanup SP refcnt issue.
- share policy-on-socket for listening socket.
- don't copy policy-on-socket at all. secpolicy no longer contain
spidx, which saves a lot of memory.
- deep-copy pcb policy if it is an ipsec policy. assign ID field to
all SPD entries. make it possible for racoon to grab SPD entry on
pcb.
- fixed the order of searching SA table for packets.
- fixed to get a security association header. a mode is always needed
to compare them.
- fixed that the incorrect time was set to
sadb_comb_{hard|soft}_usetime.
- disallow port spec for tunnel mode policy (as we don't reassemble).
- an user can define a policy-id.
- clear enc/auth key before freeing.
- fixed that the kernel crashed when key_spdacquire() was called
because key_spdacquire() had been implemented imcopletely.
- preparation for 64bit sequence number.
- maintain ordered list of SA, based on SA id.
- cleanup secasvar management; refcnt is key.c responsibility;
alloc/free is keydb.c responsibility.
- cleanup, avoid double-loop.
- use hash for spi-based lookup.
- mark persistent SP "persistent".
XXX in theory refcnt should do the right thing, however, we have
"spdflush" which would touch all SPs. another solution would be to
de-register persistent SPs from sptree.
- u_short -> u_int16_t
- reduce kernel stack usage by auto variable secasindex.
- clarify function name confusion. ipsec_*_policy ->
ipsec_*_pcbpolicy.
- avoid variable name confusion.
(struct inpcbpolicy *)pcb_sp, spp (struct secpolicy **), sp (struct
secpolicy *)
- count number of ipsec encapsulations on ipsec4_output, so that we
can tell ip_output() how to handle the packet further.
- When the value of the ul_proto is ICMP or ICMPV6, the port field in
"src" of the spidx specifies ICMP type, and the port field in "dst"
of the spidx specifies ICMP code.
- avoid from applying IPsec transport mode to the packets when the
kernel forwards the packets.
Tested by: nork
Obtained from: KAME
2003-11-04 16:02:05 +00:00
|
|
|
sp = ipsec4_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND,
|
|
|
|
flags, &error);
|
2001-06-11 12:39:29 +00:00
|
|
|
else
|
2004-02-03 18:20:55 +00:00
|
|
|
sp = ipsec4_getpolicybypcb(m, IPSEC_DIR_OUTBOUND, inp, &error);
|
2001-06-11 12:39:29 +00:00
|
|
|
|
|
|
|
if (sp == NULL) {
|
|
|
|
ipsecstat.out_inval++;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = 0;
|
|
|
|
|
|
|
|
/* check policy */
|
|
|
|
switch (sp->policy) {
|
|
|
|
case IPSEC_POLICY_DISCARD:
|
|
|
|
/*
|
|
|
|
* This packet is just discarded.
|
|
|
|
*/
|
|
|
|
ipsecstat.out_polvio++;
|
|
|
|
goto bad;
|
|
|
|
|
|
|
|
case IPSEC_POLICY_BYPASS:
|
|
|
|
case IPSEC_POLICY_NONE:
|
Initial import of RFC 2385 (TCP-MD5) digest support.
This is the first of two commits; bringing in the kernel support first.
This can be enabled by compiling a kernel with options TCP_SIGNATURE
and FAST_IPSEC.
For the uninitiated, this is a TCP option which provides for a means of
authenticating TCP sessions which came into being before IPSEC. It is
still relevant today, however, as it is used by many commercial router
vendors, particularly with BGP, and as such has become a requirement for
interconnect at many major Internet points of presence.
Several parts of the TCP and IP headers, including the segment payload,
are digested with MD5, including a shared secret. The PF_KEY interface
is used to manage the secrets using security associations in the SADB.
There is a limitation here in that as there is no way to map a TCP flow
per-port back to an SPI without polluting tcpcb or using the SPD; the
code to do the latter is unstable at this time. Therefore this code only
supports per-host keying granularity.
Whilst FAST_IPSEC is mutually exclusive with KAME IPSEC (and thus IPv6),
TCP_SIGNATURE applies only to IPv4. For the vast majority of prospective
users of this feature, this will not pose any problem.
This implementation is output-only; that is, the option is honoured when
responding to a host initiating a TCP session, but no effort is made
[yet] to authenticate inbound traffic. This is, however, sufficient to
interwork with Cisco equipment.
Tested with a Cisco 2501 running IOS 12.0(27), and Quagga 0.96.4 with
local patches. Patches for tcpdump to validate TCP-MD5 sessions are also
available from me upon request.
Sponsored by: sentex.net
2004-02-11 04:26:04 +00:00
|
|
|
case IPSEC_POLICY_TCP:
|
2001-06-11 12:39:29 +00:00
|
|
|
/* no need to do IPsec. */
|
|
|
|
goto skip_ipsec;
|
|
|
|
|
|
|
|
case IPSEC_POLICY_IPSEC:
|
|
|
|
if (sp->req == NULL) {
|
|
|
|
/* acquire a policy */
|
|
|
|
error = key_spdacquire(sp);
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPSEC_POLICY_ENTRUST:
|
|
|
|
default:
|
|
|
|
printf("ip_output: Invalid policy found. %d\n", sp->policy);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
struct ipsec_output_state state;
|
|
|
|
bzero(&state, sizeof(state));
|
|
|
|
state.m = m;
|
|
|
|
if (flags & IP_ROUTETOIF) {
|
|
|
|
state.ro = &iproute;
|
|
|
|
bzero(&iproute, sizeof(iproute));
|
|
|
|
} else
|
|
|
|
state.ro = ro;
|
|
|
|
state.dst = (struct sockaddr *)dst;
|
|
|
|
|
|
|
|
ip->ip_sum = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX
|
|
|
|
* delayed checksums are not currently compatible with IPsec
|
|
|
|
*/
|
|
|
|
if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
|
|
|
|
in_delayed_cksum(m);
|
|
|
|
m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
|
|
|
|
}
|
|
|
|
|
2002-02-18 20:35:27 +00:00
|
|
|
ip->ip_len = htons(ip->ip_len);
|
|
|
|
ip->ip_off = htons(ip->ip_off);
|
2001-06-11 12:39:29 +00:00
|
|
|
|
|
|
|
error = ipsec4_output(&state, sp, flags);
|
|
|
|
|
|
|
|
m = state.m;
|
|
|
|
if (flags & IP_ROUTETOIF) {
|
|
|
|
/*
|
|
|
|
* if we have tunnel mode SA, we may need to ignore
|
|
|
|
* IP_ROUTETOIF.
|
|
|
|
*/
|
|
|
|
if (state.ro != &iproute || state.ro->ro_rt != NULL) {
|
|
|
|
flags &= ~IP_ROUTETOIF;
|
|
|
|
ro = state.ro;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
ro = state.ro;
|
|
|
|
dst = (struct sockaddr_in *)state.dst;
|
|
|
|
if (error) {
|
|
|
|
/* mbuf is already reclaimed in ipsec4_output. */
|
2004-02-25 19:55:29 +00:00
|
|
|
m = NULL;
|
2001-06-11 12:39:29 +00:00
|
|
|
switch (error) {
|
|
|
|
case EHOSTUNREACH:
|
|
|
|
case ENETUNREACH:
|
|
|
|
case EMSGSIZE:
|
|
|
|
case ENOBUFS:
|
|
|
|
case ENOMEM:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printf("ip4_output (ipsec): error code %d\n", error);
|
|
|
|
/*fall through*/
|
|
|
|
case ENOENT:
|
|
|
|
/* don't show these error codes to the user */
|
|
|
|
error = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* be sure to update variables that are affected by ipsec4_output() */
|
|
|
|
ip = mtod(m, struct ip *);
|
|
|
|
hlen = ip->ip_hl << 2;
|
|
|
|
if (ro->ro_rt == NULL) {
|
|
|
|
if ((flags & IP_ROUTETOIF) == 0) {
|
|
|
|
printf("ip_output: "
|
|
|
|
"can't update route after IPsec processing\n");
|
|
|
|
error = EHOSTUNREACH; /*XXX*/
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
} else {
|
2004-02-16 17:05:06 +00:00
|
|
|
if (state.encap) {
|
|
|
|
ia = ifatoia(ro->ro_rt->rt_ifa);
|
|
|
|
ifp = ro->ro_rt->rt_ifp;
|
|
|
|
}
|
2001-06-11 12:39:29 +00:00
|
|
|
}
|
2004-02-16 17:05:06 +00:00
|
|
|
}
|
2001-06-11 12:39:29 +00:00
|
|
|
|
|
|
|
/* make it flipped, again. */
|
2002-02-18 20:35:27 +00:00
|
|
|
ip->ip_len = ntohs(ip->ip_len);
|
|
|
|
ip->ip_off = ntohs(ip->ip_off);
|
2001-06-11 12:39:29 +00:00
|
|
|
skip_ipsec:
|
|
|
|
#endif /*IPSEC*/
|
2002-10-16 02:25:05 +00:00
|
|
|
#ifdef FAST_IPSEC
|
|
|
|
/*
|
|
|
|
* Check the security policy (SP) for the packet and, if
|
|
|
|
* required, do IPsec-related processing. There are two
|
|
|
|
* cases here; the first time a packet is sent through
|
|
|
|
* it will be untagged and handled by ipsec4_checkpolicy.
|
|
|
|
* If the packet is resubmitted to ip_output (e.g. after
|
|
|
|
* AH, ESP, etc. processing), there will be a tag to bypass
|
|
|
|
* the lookup and related policy checking.
|
|
|
|
*/
|
|
|
|
mtag = m_tag_find(m, PACKET_TAG_IPSEC_PENDING_TDB, NULL);
|
|
|
|
s = splnet();
|
|
|
|
if (mtag != NULL) {
|
|
|
|
tdbi = (struct tdb_ident *)(mtag + 1);
|
|
|
|
sp = ipsec_getpolicy(tdbi, IPSEC_DIR_OUTBOUND);
|
|
|
|
if (sp == NULL)
|
|
|
|
error = -EINVAL; /* force silent drop */
|
|
|
|
m_tag_delete(m, mtag);
|
|
|
|
} else {
|
|
|
|
sp = ipsec4_checkpolicy(m, IPSEC_DIR_OUTBOUND, flags,
|
|
|
|
&error, inp);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* There are four return cases:
|
|
|
|
* sp != NULL apply IPsec policy
|
|
|
|
* sp == NULL, error == 0 no IPsec handling needed
|
|
|
|
* sp == NULL, error == -EINVAL discard packet w/o error
|
|
|
|
* sp == NULL, error != 0 discard packet, report error
|
|
|
|
*/
|
|
|
|
if (sp != NULL) {
|
|
|
|
/* Loop detection, check if ipsec processing already done */
|
|
|
|
KASSERT(sp->req != NULL, ("ip_output: no ipsec request"));
|
|
|
|
for (mtag = m_tag_first(m); mtag != NULL;
|
|
|
|
mtag = m_tag_next(m, mtag)) {
|
|
|
|
if (mtag->m_tag_cookie != MTAG_ABI_COMPAT)
|
|
|
|
continue;
|
|
|
|
if (mtag->m_tag_id != PACKET_TAG_IPSEC_OUT_DONE &&
|
|
|
|
mtag->m_tag_id != PACKET_TAG_IPSEC_OUT_CRYPTO_NEEDED)
|
|
|
|
continue;
|
|
|
|
/*
|
|
|
|
* Check if policy has an SA associated with it.
|
|
|
|
* This can happen when an SP has yet to acquire
|
|
|
|
* an SA; e.g. on first reference. If it occurs,
|
|
|
|
* then we let ipsec4_process_packet do its thing.
|
|
|
|
*/
|
|
|
|
if (sp->req->sav == NULL)
|
|
|
|
break;
|
|
|
|
tdbi = (struct tdb_ident *)(mtag + 1);
|
|
|
|
if (tdbi->spi == sp->req->sav->spi &&
|
|
|
|
tdbi->proto == sp->req->sav->sah->saidx.proto &&
|
2002-11-08 23:11:02 +00:00
|
|
|
bcmp(&tdbi->dst, &sp->req->sav->sah->saidx.dst,
|
2002-10-16 02:25:05 +00:00
|
|
|
sizeof (union sockaddr_union)) == 0) {
|
|
|
|
/*
|
|
|
|
* No IPsec processing is needed, free
|
|
|
|
* reference to SP.
|
|
|
|
*
|
|
|
|
* NB: null pointer to avoid free at
|
|
|
|
* done: below.
|
|
|
|
*/
|
|
|
|
KEY_FREESP(&sp), sp = NULL;
|
|
|
|
splx(s);
|
|
|
|
goto spd_done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do delayed checksums now because we send before
|
|
|
|
* this is done in the normal processing path.
|
|
|
|
*/
|
|
|
|
if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
|
|
|
|
in_delayed_cksum(m);
|
|
|
|
m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
|
|
|
|
}
|
|
|
|
|
|
|
|
ip->ip_len = htons(ip->ip_len);
|
|
|
|
ip->ip_off = htons(ip->ip_off);
|
|
|
|
|
|
|
|
/* NB: callee frees mbuf */
|
|
|
|
error = ipsec4_process_packet(m, sp->req, flags, 0);
|
2003-01-30 05:45:45 +00:00
|
|
|
/*
|
|
|
|
* Preserve KAME behaviour: ENOENT can be returned
|
|
|
|
* when an SA acquire is in progress. Don't propagate
|
|
|
|
* this to user-level; it confuses applications.
|
|
|
|
*
|
|
|
|
* XXX this will go away when the SADB is redone.
|
|
|
|
*/
|
|
|
|
if (error == ENOENT)
|
|
|
|
error = 0;
|
2002-10-16 02:25:05 +00:00
|
|
|
splx(s);
|
|
|
|
goto done;
|
|
|
|
} else {
|
|
|
|
splx(s);
|
|
|
|
|
|
|
|
if (error != 0) {
|
|
|
|
/*
|
|
|
|
* Hack: -EINVAL is used to signal that a packet
|
|
|
|
* should be silently discarded. This is typically
|
|
|
|
* because we asked key management for an SA and
|
|
|
|
* it was delayed (e.g. kicked up to IKE).
|
|
|
|
*/
|
|
|
|
if (error == -EINVAL)
|
|
|
|
error = 0;
|
|
|
|
goto bad;
|
|
|
|
} else {
|
|
|
|
/* No IPsec processing for this packet. */
|
|
|
|
}
|
|
|
|
#ifdef notyet
|
|
|
|
/*
|
|
|
|
* If deferred crypto processing is needed, check that
|
|
|
|
* the interface supports it.
|
|
|
|
*/
|
|
|
|
mtag = m_tag_find(m, PACKET_TAG_IPSEC_OUT_CRYPTO_NEEDED, NULL);
|
|
|
|
if (mtag != NULL && (ifp->if_capenable & IFCAP_IPSEC) == 0) {
|
|
|
|
/* notify IPsec to do its own crypto */
|
|
|
|
ipsp_skipcrypto_unmark((struct tdb_ident *)(mtag + 1));
|
|
|
|
error = EHOSTUNREACH;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
spd_done:
|
|
|
|
#endif /* FAST_IPSEC */
|
2001-06-11 12:39:29 +00:00
|
|
|
|
2004-08-27 15:16:24 +00:00
|
|
|
/* Jump over all PFIL processing if hooks are not active. */
|
|
|
|
if (inet_pfil_hook.ph_busy_count == -1)
|
|
|
|
goto passout;
|
|
|
|
|
|
|
|
/* Run through list of hooks for output packets. */
|
2004-08-17 22:05:54 +00:00
|
|
|
odst.s_addr = ip->ip_dst.s_addr;
|
2004-09-29 04:54:33 +00:00
|
|
|
error = pfil_run_hooks(&inet_pfil_hook, &m, ifp, PFIL_OUT, inp);
|
2003-09-23 17:54:04 +00:00
|
|
|
if (error != 0 || m == NULL)
|
|
|
|
goto done;
|
1996-08-21 21:37:07 +00:00
|
|
|
|
2004-08-17 22:05:54 +00:00
|
|
|
ip = mtod(m, struct ip *);
|
1998-12-14 18:09:13 +00:00
|
|
|
|
2004-08-17 22:05:54 +00:00
|
|
|
/* See if destination IP address was changed by packet filter. */
|
|
|
|
if (odst.s_addr != ip->ip_dst.s_addr) {
|
|
|
|
m->m_flags |= M_SKIP_FIREWALL;
|
2004-09-13 17:09:06 +00:00
|
|
|
/* If destination is now ourself drop to ip_input(). */
|
2004-08-17 22:05:54 +00:00
|
|
|
if (in_localip(ip->ip_dst)) {
|
|
|
|
m->m_flags |= M_FASTFWD_OURS;
|
|
|
|
if (m->m_pkthdr.rcvif == NULL)
|
2004-08-27 15:39:34 +00:00
|
|
|
m->m_pkthdr.rcvif = loif;
|
2004-08-17 22:05:54 +00:00
|
|
|
if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
|
|
|
|
m->m_pkthdr.csum_flags |=
|
|
|
|
CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
|
|
|
|
m->m_pkthdr.csum_data = 0xffff;
|
|
|
|
}
|
|
|
|
m->m_pkthdr.csum_flags |=
|
|
|
|
CSUM_IP_CHECKED | CSUM_IP_VALID;
|
Remove (almost all) global variables that were used to hold
packet forwarding state ("annotations") during ip processing.
The code is considerably cleaner now.
The variables removed by this change are:
ip_divert_cookie used by divert sockets
ip_fw_fwd_addr used for transparent ip redirection
last_pkt used by dynamic pipes in dummynet
Removal of the first two has been done by carrying the annotations
into volatile structs prepended to the mbuf chains, and adding
appropriate code to add/remove annotations in the routines which
make use of them, i.e. ip_input(), ip_output(), tcp_input(),
bdg_forward(), ether_demux(), ether_output_frame(), div_output().
On passing, remove a bug in divert handling of fragmented packet.
Now it is the fragment at offset 0 which sets the divert status of
the whole packet, whereas formerly it was the last incoming fragment
to decide.
Removal of last_pkt required a change in the interface of ip_fw_chk()
and dummynet_io(). On passing, use the same mechanism for dummynet
annotations and for divert/forward annotations.
option IPFIREWALL_FORWARD is effectively useless, the code to
implement it is very small and is now in by default to avoid the
obfuscation of conditionally compiled code.
NOTES:
* there is at least one global variable left, sro_fwd, in ip_output().
I am not sure if/how this can be removed.
* I have deliberately avoided gratuitous style changes in this commit
to avoid cluttering the diffs. Minor stule cleanup will likely be
necessary
* this commit only focused on the IP layer. I am sure there is a
number of global variables used in the TCP and maybe UDP stack.
* despite the number of files touched, there are absolutely no API's
or data structures changed by this commit (except the interfaces of
ip_fw_chk() and dummynet_io(), which are internal anyways), so
an MFC is quite safe and unintrusive (and desirable, given the
improved readability of the code).
MFC after: 10 days
2002-06-22 11:51:02 +00:00
|
|
|
|
2004-08-17 22:05:54 +00:00
|
|
|
error = netisr_queue(NETISR_IP, m);
|
2001-12-14 19:34:11 +00:00
|
|
|
goto done;
|
2004-08-17 22:05:54 +00:00
|
|
|
} else
|
2004-09-13 17:09:06 +00:00
|
|
|
goto again; /* Redo the routing table lookup. */
|
2004-08-17 22:05:54 +00:00
|
|
|
}
|
Remove (almost all) global variables that were used to hold
packet forwarding state ("annotations") during ip processing.
The code is considerably cleaner now.
The variables removed by this change are:
ip_divert_cookie used by divert sockets
ip_fw_fwd_addr used for transparent ip redirection
last_pkt used by dynamic pipes in dummynet
Removal of the first two has been done by carrying the annotations
into volatile structs prepended to the mbuf chains, and adding
appropriate code to add/remove annotations in the routines which
make use of them, i.e. ip_input(), ip_output(), tcp_input(),
bdg_forward(), ether_demux(), ether_output_frame(), div_output().
On passing, remove a bug in divert handling of fragmented packet.
Now it is the fragment at offset 0 which sets the divert status of
the whole packet, whereas formerly it was the last incoming fragment
to decide.
Removal of last_pkt required a change in the interface of ip_fw_chk()
and dummynet_io(). On passing, use the same mechanism for dummynet
annotations and for divert/forward annotations.
option IPFIREWALL_FORWARD is effectively useless, the code to
implement it is very small and is now in by default to avoid the
obfuscation of conditionally compiled code.
NOTES:
* there is at least one global variable left, sro_fwd, in ip_output().
I am not sure if/how this can be removed.
* I have deliberately avoided gratuitous style changes in this commit
to avoid cluttering the diffs. Minor stule cleanup will likely be
necessary
* this commit only focused on the IP layer. I am sure there is a
number of global variables used in the TCP and maybe UDP stack.
* despite the number of files touched, there are absolutely no API's
or data structures changed by this commit (except the interfaces of
ip_fw_chk() and dummynet_io(), which are internal anyways), so
an MFC is quite safe and unintrusive (and desirable, given the
improved readability of the code).
MFC after: 10 days
2002-06-22 11:51:02 +00:00
|
|
|
|
2004-08-17 22:05:54 +00:00
|
|
|
#ifdef IPFIREWALL_FORWARD
|
|
|
|
/* See if local, if yes, send it to netisr with IP_FASTFWD_OURS. */
|
|
|
|
if (m->m_flags & M_FASTFWD_OURS) {
|
|
|
|
if (m->m_pkthdr.rcvif == NULL)
|
2004-08-27 15:39:34 +00:00
|
|
|
m->m_pkthdr.rcvif = loif;
|
2004-08-17 22:05:54 +00:00
|
|
|
if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
|
|
|
|
m->m_pkthdr.csum_flags |=
|
|
|
|
CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
|
|
|
|
m->m_pkthdr.csum_data = 0xffff;
|
1997-06-02 05:02:37 +00:00
|
|
|
}
|
2004-08-17 22:05:54 +00:00
|
|
|
m->m_pkthdr.csum_flags |=
|
|
|
|
CSUM_IP_CHECKED | CSUM_IP_VALID;
|
1999-12-06 00:43:07 +00:00
|
|
|
|
2004-08-17 22:05:54 +00:00
|
|
|
error = netisr_queue(NETISR_IP, m);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
/* Or forward to some other address? */
|
|
|
|
fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL);
|
|
|
|
if (fwd_tag) {
|
2005-02-22 17:40:40 +00:00
|
|
|
#ifndef IPFIREWALL_FORWARD_EXTENDED
|
2004-08-17 22:05:54 +00:00
|
|
|
if (!in_localip(ip->ip_src) && !in_localaddr(ip->ip_dst)) {
|
2005-02-22 17:40:40 +00:00
|
|
|
#endif
|
2004-08-17 22:05:54 +00:00
|
|
|
dst = (struct sockaddr_in *)&ro->ro_dst;
|
|
|
|
bcopy((fwd_tag+1), dst, sizeof(struct sockaddr_in));
|
|
|
|
m->m_flags |= M_SKIP_FIREWALL;
|
|
|
|
m_tag_delete(m, fwd_tag);
|
|
|
|
goto again;
|
2005-02-22 17:40:40 +00:00
|
|
|
#ifndef IPFIREWALL_FORWARD_EXTENDED
|
2004-08-17 22:05:54 +00:00
|
|
|
} else {
|
|
|
|
m_tag_delete(m, fwd_tag);
|
|
|
|
/* Continue. */
|
1996-07-10 19:44:30 +00:00
|
|
|
}
|
1998-12-14 18:09:13 +00:00
|
|
|
#endif
|
2005-02-22 17:40:40 +00:00
|
|
|
}
|
|
|
|
#endif /* IPFIREWALL_FORWARD */
|
1998-12-14 18:09:13 +00:00
|
|
|
|
2004-08-27 15:16:24 +00:00
|
|
|
passout:
|
2002-02-15 12:19:03 +00:00
|
|
|
/* 127/8 must not appear on wire - RFC1122. */
|
|
|
|
if ((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET ||
|
|
|
|
(ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) {
|
|
|
|
if ((ifp->if_flags & IFF_LOOPBACK) == 0) {
|
|
|
|
ipstat.ips_badaddr++;
|
|
|
|
error = EADDRNOTAVAIL;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
RFC768 (UDP) requires that "if the computed checksum is zero, it
is transmitted as all ones". This got broken after introduction
of delayed checksums as follows. Some guys (including Jonathan)
think that it is allowed to transmit all ones in place of a zero
checksum for TCP the same way as for UDP. (The discussion still
takes place on -net.) Thus, the 0 -> 0xffff checksum fixup was
first moved from udp_output() (see udp_usrreq.c, 1.64 -> 1.65)
to in_cksum_skip() (see sys/i386/i386/in_cksum.c, 1.17 -> 1.18,
INVERT expression). Besides that I disagree that it is valid for
TCP, there was no real problem until in_cksum.c,v 1.20, where the
in_cksum() was made just a special version of in_cksum_skip().
The side effect was that now every incoming IP datagram failed to
pass the checksum test (in_cksum() returned 0xffff when it should
actually return zero). It was fixed next day in revision 1.21,
by removing the INVERT expression. The latter also broke the
0 -> 0xffff fixup for UDP checksums.
Before this change:
: tcpdump: listening on lo0
: 127.0.0.1.33005 > 127.0.0.1.33006: udp 0 (ttl 64, id 1)
: 4500 001c 0001 0000 4011 7cce 7f00 0001
: 7f00 0001 80ed 80ee 0008 0000
After this change:
: tcpdump: listening on lo0
: 127.0.0.1.33005 > 127.0.0.1.33006: udp 0 (ttl 64, id 1)
: 4500 001c 0001 0000 4011 7cce 7f00 0001
: 7f00 0001 80ed 80ee 0008 ffff
2001-03-13 17:07:06 +00:00
|
|
|
m->m_pkthdr.csum_flags |= CSUM_IP;
|
|
|
|
sw_csum = m->m_pkthdr.csum_flags & ~ifp->if_hwassist;
|
2000-03-27 19:14:27 +00:00
|
|
|
if (sw_csum & CSUM_DELAY_DATA) {
|
|
|
|
in_delayed_cksum(m);
|
|
|
|
sw_csum &= ~CSUM_DELAY_DATA;
|
|
|
|
}
|
RFC768 (UDP) requires that "if the computed checksum is zero, it
is transmitted as all ones". This got broken after introduction
of delayed checksums as follows. Some guys (including Jonathan)
think that it is allowed to transmit all ones in place of a zero
checksum for TCP the same way as for UDP. (The discussion still
takes place on -net.) Thus, the 0 -> 0xffff checksum fixup was
first moved from udp_output() (see udp_usrreq.c, 1.64 -> 1.65)
to in_cksum_skip() (see sys/i386/i386/in_cksum.c, 1.17 -> 1.18,
INVERT expression). Besides that I disagree that it is valid for
TCP, there was no real problem until in_cksum.c,v 1.20, where the
in_cksum() was made just a special version of in_cksum_skip().
The side effect was that now every incoming IP datagram failed to
pass the checksum test (in_cksum() returned 0xffff when it should
actually return zero). It was fixed next day in revision 1.21,
by removing the INVERT expression. The latter also broke the
0 -> 0xffff fixup for UDP checksums.
Before this change:
: tcpdump: listening on lo0
: 127.0.0.1.33005 > 127.0.0.1.33006: udp 0 (ttl 64, id 1)
: 4500 001c 0001 0000 4011 7cce 7f00 0001
: 7f00 0001 80ed 80ee 0008 0000
After this change:
: tcpdump: listening on lo0
: 127.0.0.1.33005 > 127.0.0.1.33006: udp 0 (ttl 64, id 1)
: 4500 001c 0001 0000 4011 7cce 7f00 0001
: 7f00 0001 80ed 80ee 0008 ffff
2001-03-13 17:07:06 +00:00
|
|
|
m->m_pkthdr.csum_flags &= ifp->if_hwassist;
|
2000-03-27 19:14:27 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
2000-03-27 19:14:27 +00:00
|
|
|
* If small enough for interface, or the interface will take
|
|
|
|
* care of the fragmentation for us, can just send directly.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
2003-11-12 23:35:40 +00:00
|
|
|
if (ip->ip_len <= ifp->if_mtu || (ifp->if_hwassist & CSUM_FRAGMENT &&
|
|
|
|
((ip->ip_off & IP_DF) == 0))) {
|
2002-02-18 20:35:27 +00:00
|
|
|
ip->ip_len = htons(ip->ip_len);
|
|
|
|
ip->ip_off = htons(ip->ip_off);
|
1994-05-24 10:09:53 +00:00
|
|
|
ip->ip_sum = 0;
|
2002-10-20 22:52:07 +00:00
|
|
|
if (sw_csum & CSUM_DELAY_IP)
|
|
|
|
ip->ip_sum = in_cksum(m, hlen);
|
2000-10-19 23:15:54 +00:00
|
|
|
|
|
|
|
/* Record statistics for this interface address. */
|
2001-07-23 16:50:01 +00:00
|
|
|
if (!(flags & IP_FORWARDING) && ia) {
|
2000-10-19 23:15:54 +00:00
|
|
|
ia->ia_ifa.if_opackets++;
|
|
|
|
ia->ia_ifa.if_obytes += m->m_pkthdr.len;
|
|
|
|
}
|
|
|
|
|
2001-06-11 12:39:29 +00:00
|
|
|
#ifdef IPSEC
|
|
|
|
/* clean ipsec history once it goes out of the node */
|
|
|
|
ipsec_delaux(m);
|
|
|
|
#endif
|
|
|
|
|
2003-04-12 06:11:46 +00:00
|
|
|
#ifdef MBUF_STRESS_TEST
|
2003-09-01 05:55:37 +00:00
|
|
|
if (mbuf_frag_size && m->m_pkthdr.len > mbuf_frag_size)
|
|
|
|
m = m_fragment(m, M_DONTWAIT, mbuf_frag_size);
|
2003-03-25 05:45:05 +00:00
|
|
|
#endif
|
1994-05-24 10:09:53 +00:00
|
|
|
error = (*ifp->if_output)(ifp, m,
|
|
|
|
(struct sockaddr *)dst, ro->ro_rt);
|
|
|
|
goto done;
|
|
|
|
}
|
2003-08-07 18:16:59 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
if (ip->ip_off & IP_DF) {
|
|
|
|
error = EMSGSIZE;
|
1995-10-16 18:21:26 +00:00
|
|
|
/*
|
|
|
|
* This case can happen if the user changed the MTU
|
|
|
|
* of an interface after enabling IP on it. Because
|
|
|
|
* most netifs don't keep track of routes pointing to
|
|
|
|
* them, there is no way for one to update all its
|
|
|
|
* routes when the MTU is changed.
|
|
|
|
*/
|
2003-08-07 18:16:59 +00:00
|
|
|
if ((ro->ro_rt->rt_flags & (RTF_UP | RTF_HOST)) &&
|
|
|
|
(ro->ro_rt->rt_rmx.rmx_mtu > ifp->if_mtu)) {
|
1995-10-16 18:21:26 +00:00
|
|
|
ro->ro_rt->rt_rmx.rmx_mtu = ifp->if_mtu;
|
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
ipstat.ips_cantfrag++;
|
|
|
|
goto bad;
|
|
|
|
}
|
2003-08-07 18:16:59 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Too large for interface; fragment if possible. If successful,
|
|
|
|
* on return, m will point to a list of packets to be sent.
|
|
|
|
*/
|
|
|
|
error = ip_fragment(ip, &m, ifp->if_mtu, ifp->if_hwassist, sw_csum);
|
|
|
|
if (error)
|
1994-05-24 10:09:53 +00:00
|
|
|
goto bad;
|
2003-08-07 18:16:59 +00:00
|
|
|
for (; m; m = m0) {
|
|
|
|
m0 = m->m_nextpkt;
|
|
|
|
m->m_nextpkt = 0;
|
|
|
|
#ifdef IPSEC
|
|
|
|
/* clean ipsec history once it goes out of the node */
|
|
|
|
ipsec_delaux(m);
|
|
|
|
#endif
|
|
|
|
if (error == 0) {
|
|
|
|
/* Record statistics for this interface address. */
|
|
|
|
if (ia != NULL) {
|
|
|
|
ia->ia_ifa.if_opackets++;
|
|
|
|
ia->ia_ifa.if_obytes += m->m_pkthdr.len;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = (*ifp->if_output)(ifp, m,
|
|
|
|
(struct sockaddr *)dst, ro->ro_rt);
|
|
|
|
} else
|
|
|
|
m_freem(m);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
2003-08-07 18:16:59 +00:00
|
|
|
if (error == 0)
|
|
|
|
ipstat.ips_fragmented++;
|
|
|
|
|
|
|
|
done:
|
|
|
|
if (ro == &iproute && ro->ro_rt) {
|
|
|
|
RTFREE(ro->ro_rt);
|
2004-02-25 19:55:29 +00:00
|
|
|
}
|
2003-11-14 21:48:57 +00:00
|
|
|
#ifdef IPSEC
|
2003-08-07 18:16:59 +00:00
|
|
|
if (sp != NULL) {
|
|
|
|
KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
|
|
|
|
printf("DP ip_output call free SP:%p\n", sp));
|
|
|
|
key_freesp(sp);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef FAST_IPSEC
|
|
|
|
if (sp != NULL)
|
|
|
|
KEY_FREESP(&sp);
|
|
|
|
#endif
|
|
|
|
return (error);
|
|
|
|
bad:
|
|
|
|
m_freem(m);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a chain of fragments which fit the given mtu. m_frag points to the
|
|
|
|
* mbuf to be fragmented; on return it points to the chain with the fragments.
|
|
|
|
* Return 0 if no error. If error, m_frag may contain a partially built
|
|
|
|
* chain of fragments that should be freed by the caller.
|
|
|
|
*
|
|
|
|
* if_hwassist_flags is the hw offload capabilities (see if_data.ifi_hwassist)
|
|
|
|
* sw_csum contains the delayed checksums flags (e.g., CSUM_DELAY_IP).
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
ip_fragment(struct ip *ip, struct mbuf **m_frag, int mtu,
|
|
|
|
u_long if_hwassist_flags, int sw_csum)
|
|
|
|
{
|
|
|
|
int error = 0;
|
|
|
|
int hlen = ip->ip_hl << 2;
|
|
|
|
int len = (mtu - hlen) & ~7; /* size of payload in each fragment */
|
|
|
|
int off;
|
|
|
|
struct mbuf *m0 = *m_frag; /* the original packet */
|
|
|
|
int firstlen;
|
|
|
|
struct mbuf **mnext;
|
|
|
|
int nfrags;
|
|
|
|
|
|
|
|
if (ip->ip_off & IP_DF) { /* Fragmentation not allowed */
|
|
|
|
ipstat.ips_cantfrag++;
|
|
|
|
return EMSGSIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Must be able to put at least 8 bytes per fragment.
|
|
|
|
*/
|
|
|
|
if (len < 8)
|
|
|
|
return EMSGSIZE;
|
|
|
|
|
2000-03-27 19:14:27 +00:00
|
|
|
/*
|
2003-08-07 18:16:59 +00:00
|
|
|
* If the interface will not calculate checksums on
|
2000-03-27 19:14:27 +00:00
|
|
|
* fragmented packets, then do it here.
|
|
|
|
*/
|
2003-08-07 18:16:59 +00:00
|
|
|
if (m0->m_pkthdr.csum_flags & CSUM_DELAY_DATA &&
|
|
|
|
(if_hwassist_flags & CSUM_IP_FRAGS) == 0) {
|
|
|
|
in_delayed_cksum(m0);
|
|
|
|
m0->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
|
2000-03-27 19:14:27 +00:00
|
|
|
}
|
|
|
|
|
At long last, commit the zero copy sockets code.
MAKEDEV: Add MAKEDEV glue for the ti(4) device nodes.
ti.4: Update the ti(4) man page to include information on the
TI_JUMBO_HDRSPLIT and TI_PRIVATE_JUMBOS kernel options,
and also include information about the new character
device interface and the associated ioctls.
man9/Makefile: Add jumbo.9 and zero_copy.9 man pages and associated
links.
jumbo.9: New man page describing the jumbo buffer allocator
interface and operation.
zero_copy.9: New man page describing the general characteristics of
the zero copy send and receive code, and what an
application author should do to take advantage of the
zero copy functionality.
NOTES: Add entries for ZERO_COPY_SOCKETS, TI_PRIVATE_JUMBOS,
TI_JUMBO_HDRSPLIT, MSIZE, and MCLSHIFT.
conf/files: Add uipc_jumbo.c and uipc_cow.c.
conf/options: Add the 5 options mentioned above.
kern_subr.c: Receive side zero copy implementation. This takes
"disposable" pages attached to an mbuf, gives them to
a user process, and then recycles the user's page.
This is only active when ZERO_COPY_SOCKETS is turned on
and the kern.ipc.zero_copy.receive sysctl variable is
set to 1.
uipc_cow.c: Send side zero copy functions. Takes a page written
by the user and maps it copy on write and assigns it
kernel virtual address space. Removes copy on write
mapping once the buffer has been freed by the network
stack.
uipc_jumbo.c: Jumbo disposable page allocator code. This allocates
(optionally) disposable pages for network drivers that
want to give the user the option of doing zero copy
receive.
uipc_socket.c: Add kern.ipc.zero_copy.{send,receive} sysctls that are
enabled if ZERO_COPY_SOCKETS is turned on.
Add zero copy send support to sosend() -- pages get
mapped into the kernel instead of getting copied if
they meet size and alignment restrictions.
uipc_syscalls.c:Un-staticize some of the sf* functions so that they
can be used elsewhere. (uipc_cow.c)
if_media.c: In the SIOCGIFMEDIA ioctl in ifmedia_ioctl(), avoid
calling malloc() with M_WAITOK. Return an error if
the M_NOWAIT malloc fails.
The ti(4) driver and the wi(4) driver, at least, call
this with a mutex held. This causes witness warnings
for 'ifconfig -a' with a wi(4) or ti(4) board in the
system. (I've only verified for ti(4)).
ip_output.c: Fragment large datagrams so that each segment contains
a multiple of PAGE_SIZE amount of data plus headers.
This allows the receiver to potentially do page
flipping on receives.
if_ti.c: Add zero copy receive support to the ti(4) driver. If
TI_PRIVATE_JUMBOS is not defined, it now uses the
jumbo(9) buffer allocator for jumbo receive buffers.
Add a new character device interface for the ti(4)
driver for the new debugging interface. This allows
(a patched version of) gdb to talk to the Tigon board
and debug the firmware. There are also a few additional
debugging ioctls available through this interface.
Add header splitting support to the ti(4) driver.
Tweak some of the default interrupt coalescing
parameters to more useful defaults.
Add hooks for supporting transmit flow control, but
leave it turned off with a comment describing why it
is turned off.
if_tireg.h: Change the firmware rev to 12.4.11, since we're really
at 12.4.11 plus fixes from 12.4.13.
Add defines needed for debugging.
Remove the ti_stats structure, it is now defined in
sys/tiio.h.
ti_fw.h: 12.4.11 firmware.
ti_fw2.h: 12.4.11 firmware, plus selected fixes from 12.4.13,
and my header splitting patches. Revision 12.4.13
doesn't handle 10/100 negotiation properly. (This
firmware is the same as what was in the tree previously,
with the addition of header splitting support.)
sys/jumbo.h: Jumbo buffer allocator interface.
sys/mbuf.h: Add a new external mbuf type, EXT_DISPOSABLE, to
indicate that the payload buffer can be thrown away /
flipped to a userland process.
socketvar.h: Add prototype for socow_setup.
tiio.h: ioctl interface to the character portion of the ti(4)
driver, plus associated structure/type definitions.
uio.h: Change prototype for uiomoveco() so that we'll know
whether the source page is disposable.
ufs_readwrite.c:Update for new prototype of uiomoveco().
vm_fault.c: In vm_fault(), check to see whether we need to do a page
based copy on write fault.
vm_object.c: Add a new function, vm_object_allocate_wait(). This
does the same thing that vm_object allocate does, except
that it gives the caller the opportunity to specify whether
it should wait on the uma_zalloc() of the object structre.
This allows vm objects to be allocated while holding a
mutex. (Without generating WITNESS warnings.)
vm_object_allocate() is implemented as a call to
vm_object_allocate_wait() with the malloc flag set to
M_WAITOK.
vm_object.h: Add prototype for vm_object_allocate_wait().
vm_page.c: Add page-based copy on write setup, clear and fault
routines.
vm_page.h: Add page based COW function prototypes and variable in
the vm_page structure.
Many thanks to Drew Gallatin, who wrote the zero copy send and receive
code, and to all the other folks who have tested and reviewed this code
over the years.
2002-06-26 03:37:47 +00:00
|
|
|
if (len > PAGE_SIZE) {
|
|
|
|
/*
|
2003-08-07 18:16:59 +00:00
|
|
|
* Fragment large datagrams such that each segment
|
At long last, commit the zero copy sockets code.
MAKEDEV: Add MAKEDEV glue for the ti(4) device nodes.
ti.4: Update the ti(4) man page to include information on the
TI_JUMBO_HDRSPLIT and TI_PRIVATE_JUMBOS kernel options,
and also include information about the new character
device interface and the associated ioctls.
man9/Makefile: Add jumbo.9 and zero_copy.9 man pages and associated
links.
jumbo.9: New man page describing the jumbo buffer allocator
interface and operation.
zero_copy.9: New man page describing the general characteristics of
the zero copy send and receive code, and what an
application author should do to take advantage of the
zero copy functionality.
NOTES: Add entries for ZERO_COPY_SOCKETS, TI_PRIVATE_JUMBOS,
TI_JUMBO_HDRSPLIT, MSIZE, and MCLSHIFT.
conf/files: Add uipc_jumbo.c and uipc_cow.c.
conf/options: Add the 5 options mentioned above.
kern_subr.c: Receive side zero copy implementation. This takes
"disposable" pages attached to an mbuf, gives them to
a user process, and then recycles the user's page.
This is only active when ZERO_COPY_SOCKETS is turned on
and the kern.ipc.zero_copy.receive sysctl variable is
set to 1.
uipc_cow.c: Send side zero copy functions. Takes a page written
by the user and maps it copy on write and assigns it
kernel virtual address space. Removes copy on write
mapping once the buffer has been freed by the network
stack.
uipc_jumbo.c: Jumbo disposable page allocator code. This allocates
(optionally) disposable pages for network drivers that
want to give the user the option of doing zero copy
receive.
uipc_socket.c: Add kern.ipc.zero_copy.{send,receive} sysctls that are
enabled if ZERO_COPY_SOCKETS is turned on.
Add zero copy send support to sosend() -- pages get
mapped into the kernel instead of getting copied if
they meet size and alignment restrictions.
uipc_syscalls.c:Un-staticize some of the sf* functions so that they
can be used elsewhere. (uipc_cow.c)
if_media.c: In the SIOCGIFMEDIA ioctl in ifmedia_ioctl(), avoid
calling malloc() with M_WAITOK. Return an error if
the M_NOWAIT malloc fails.
The ti(4) driver and the wi(4) driver, at least, call
this with a mutex held. This causes witness warnings
for 'ifconfig -a' with a wi(4) or ti(4) board in the
system. (I've only verified for ti(4)).
ip_output.c: Fragment large datagrams so that each segment contains
a multiple of PAGE_SIZE amount of data plus headers.
This allows the receiver to potentially do page
flipping on receives.
if_ti.c: Add zero copy receive support to the ti(4) driver. If
TI_PRIVATE_JUMBOS is not defined, it now uses the
jumbo(9) buffer allocator for jumbo receive buffers.
Add a new character device interface for the ti(4)
driver for the new debugging interface. This allows
(a patched version of) gdb to talk to the Tigon board
and debug the firmware. There are also a few additional
debugging ioctls available through this interface.
Add header splitting support to the ti(4) driver.
Tweak some of the default interrupt coalescing
parameters to more useful defaults.
Add hooks for supporting transmit flow control, but
leave it turned off with a comment describing why it
is turned off.
if_tireg.h: Change the firmware rev to 12.4.11, since we're really
at 12.4.11 plus fixes from 12.4.13.
Add defines needed for debugging.
Remove the ti_stats structure, it is now defined in
sys/tiio.h.
ti_fw.h: 12.4.11 firmware.
ti_fw2.h: 12.4.11 firmware, plus selected fixes from 12.4.13,
and my header splitting patches. Revision 12.4.13
doesn't handle 10/100 negotiation properly. (This
firmware is the same as what was in the tree previously,
with the addition of header splitting support.)
sys/jumbo.h: Jumbo buffer allocator interface.
sys/mbuf.h: Add a new external mbuf type, EXT_DISPOSABLE, to
indicate that the payload buffer can be thrown away /
flipped to a userland process.
socketvar.h: Add prototype for socow_setup.
tiio.h: ioctl interface to the character portion of the ti(4)
driver, plus associated structure/type definitions.
uio.h: Change prototype for uiomoveco() so that we'll know
whether the source page is disposable.
ufs_readwrite.c:Update for new prototype of uiomoveco().
vm_fault.c: In vm_fault(), check to see whether we need to do a page
based copy on write fault.
vm_object.c: Add a new function, vm_object_allocate_wait(). This
does the same thing that vm_object allocate does, except
that it gives the caller the opportunity to specify whether
it should wait on the uma_zalloc() of the object structre.
This allows vm objects to be allocated while holding a
mutex. (Without generating WITNESS warnings.)
vm_object_allocate() is implemented as a call to
vm_object_allocate_wait() with the malloc flag set to
M_WAITOK.
vm_object.h: Add prototype for vm_object_allocate_wait().
vm_page.c: Add page-based copy on write setup, clear and fault
routines.
vm_page.h: Add page based COW function prototypes and variable in
the vm_page structure.
Many thanks to Drew Gallatin, who wrote the zero copy send and receive
code, and to all the other folks who have tested and reviewed this code
over the years.
2002-06-26 03:37:47 +00:00
|
|
|
* contains a multiple of PAGE_SIZE amount of data,
|
|
|
|
* plus headers. This enables a receiver to perform
|
|
|
|
* page-flipping zero-copy optimizations.
|
2003-08-07 18:16:59 +00:00
|
|
|
*
|
|
|
|
* XXX When does this help given that sender and receiver
|
|
|
|
* could have different page sizes, and also mtu could
|
|
|
|
* be less than the receiver's page size ?
|
At long last, commit the zero copy sockets code.
MAKEDEV: Add MAKEDEV glue for the ti(4) device nodes.
ti.4: Update the ti(4) man page to include information on the
TI_JUMBO_HDRSPLIT and TI_PRIVATE_JUMBOS kernel options,
and also include information about the new character
device interface and the associated ioctls.
man9/Makefile: Add jumbo.9 and zero_copy.9 man pages and associated
links.
jumbo.9: New man page describing the jumbo buffer allocator
interface and operation.
zero_copy.9: New man page describing the general characteristics of
the zero copy send and receive code, and what an
application author should do to take advantage of the
zero copy functionality.
NOTES: Add entries for ZERO_COPY_SOCKETS, TI_PRIVATE_JUMBOS,
TI_JUMBO_HDRSPLIT, MSIZE, and MCLSHIFT.
conf/files: Add uipc_jumbo.c and uipc_cow.c.
conf/options: Add the 5 options mentioned above.
kern_subr.c: Receive side zero copy implementation. This takes
"disposable" pages attached to an mbuf, gives them to
a user process, and then recycles the user's page.
This is only active when ZERO_COPY_SOCKETS is turned on
and the kern.ipc.zero_copy.receive sysctl variable is
set to 1.
uipc_cow.c: Send side zero copy functions. Takes a page written
by the user and maps it copy on write and assigns it
kernel virtual address space. Removes copy on write
mapping once the buffer has been freed by the network
stack.
uipc_jumbo.c: Jumbo disposable page allocator code. This allocates
(optionally) disposable pages for network drivers that
want to give the user the option of doing zero copy
receive.
uipc_socket.c: Add kern.ipc.zero_copy.{send,receive} sysctls that are
enabled if ZERO_COPY_SOCKETS is turned on.
Add zero copy send support to sosend() -- pages get
mapped into the kernel instead of getting copied if
they meet size and alignment restrictions.
uipc_syscalls.c:Un-staticize some of the sf* functions so that they
can be used elsewhere. (uipc_cow.c)
if_media.c: In the SIOCGIFMEDIA ioctl in ifmedia_ioctl(), avoid
calling malloc() with M_WAITOK. Return an error if
the M_NOWAIT malloc fails.
The ti(4) driver and the wi(4) driver, at least, call
this with a mutex held. This causes witness warnings
for 'ifconfig -a' with a wi(4) or ti(4) board in the
system. (I've only verified for ti(4)).
ip_output.c: Fragment large datagrams so that each segment contains
a multiple of PAGE_SIZE amount of data plus headers.
This allows the receiver to potentially do page
flipping on receives.
if_ti.c: Add zero copy receive support to the ti(4) driver. If
TI_PRIVATE_JUMBOS is not defined, it now uses the
jumbo(9) buffer allocator for jumbo receive buffers.
Add a new character device interface for the ti(4)
driver for the new debugging interface. This allows
(a patched version of) gdb to talk to the Tigon board
and debug the firmware. There are also a few additional
debugging ioctls available through this interface.
Add header splitting support to the ti(4) driver.
Tweak some of the default interrupt coalescing
parameters to more useful defaults.
Add hooks for supporting transmit flow control, but
leave it turned off with a comment describing why it
is turned off.
if_tireg.h: Change the firmware rev to 12.4.11, since we're really
at 12.4.11 plus fixes from 12.4.13.
Add defines needed for debugging.
Remove the ti_stats structure, it is now defined in
sys/tiio.h.
ti_fw.h: 12.4.11 firmware.
ti_fw2.h: 12.4.11 firmware, plus selected fixes from 12.4.13,
and my header splitting patches. Revision 12.4.13
doesn't handle 10/100 negotiation properly. (This
firmware is the same as what was in the tree previously,
with the addition of header splitting support.)
sys/jumbo.h: Jumbo buffer allocator interface.
sys/mbuf.h: Add a new external mbuf type, EXT_DISPOSABLE, to
indicate that the payload buffer can be thrown away /
flipped to a userland process.
socketvar.h: Add prototype for socow_setup.
tiio.h: ioctl interface to the character portion of the ti(4)
driver, plus associated structure/type definitions.
uio.h: Change prototype for uiomoveco() so that we'll know
whether the source page is disposable.
ufs_readwrite.c:Update for new prototype of uiomoveco().
vm_fault.c: In vm_fault(), check to see whether we need to do a page
based copy on write fault.
vm_object.c: Add a new function, vm_object_allocate_wait(). This
does the same thing that vm_object allocate does, except
that it gives the caller the opportunity to specify whether
it should wait on the uma_zalloc() of the object structre.
This allows vm objects to be allocated while holding a
mutex. (Without generating WITNESS warnings.)
vm_object_allocate() is implemented as a call to
vm_object_allocate_wait() with the malloc flag set to
M_WAITOK.
vm_object.h: Add prototype for vm_object_allocate_wait().
vm_page.c: Add page-based copy on write setup, clear and fault
routines.
vm_page.h: Add page based COW function prototypes and variable in
the vm_page structure.
Many thanks to Drew Gallatin, who wrote the zero copy send and receive
code, and to all the other folks who have tested and reviewed this code
over the years.
2002-06-26 03:37:47 +00:00
|
|
|
*/
|
|
|
|
int newlen;
|
2003-08-07 18:16:59 +00:00
|
|
|
struct mbuf *m;
|
|
|
|
|
|
|
|
for (m = m0, off = 0; m && (off+m->m_len) <= mtu; m = m->m_next)
|
|
|
|
off += m->m_len;
|
At long last, commit the zero copy sockets code.
MAKEDEV: Add MAKEDEV glue for the ti(4) device nodes.
ti.4: Update the ti(4) man page to include information on the
TI_JUMBO_HDRSPLIT and TI_PRIVATE_JUMBOS kernel options,
and also include information about the new character
device interface and the associated ioctls.
man9/Makefile: Add jumbo.9 and zero_copy.9 man pages and associated
links.
jumbo.9: New man page describing the jumbo buffer allocator
interface and operation.
zero_copy.9: New man page describing the general characteristics of
the zero copy send and receive code, and what an
application author should do to take advantage of the
zero copy functionality.
NOTES: Add entries for ZERO_COPY_SOCKETS, TI_PRIVATE_JUMBOS,
TI_JUMBO_HDRSPLIT, MSIZE, and MCLSHIFT.
conf/files: Add uipc_jumbo.c and uipc_cow.c.
conf/options: Add the 5 options mentioned above.
kern_subr.c: Receive side zero copy implementation. This takes
"disposable" pages attached to an mbuf, gives them to
a user process, and then recycles the user's page.
This is only active when ZERO_COPY_SOCKETS is turned on
and the kern.ipc.zero_copy.receive sysctl variable is
set to 1.
uipc_cow.c: Send side zero copy functions. Takes a page written
by the user and maps it copy on write and assigns it
kernel virtual address space. Removes copy on write
mapping once the buffer has been freed by the network
stack.
uipc_jumbo.c: Jumbo disposable page allocator code. This allocates
(optionally) disposable pages for network drivers that
want to give the user the option of doing zero copy
receive.
uipc_socket.c: Add kern.ipc.zero_copy.{send,receive} sysctls that are
enabled if ZERO_COPY_SOCKETS is turned on.
Add zero copy send support to sosend() -- pages get
mapped into the kernel instead of getting copied if
they meet size and alignment restrictions.
uipc_syscalls.c:Un-staticize some of the sf* functions so that they
can be used elsewhere. (uipc_cow.c)
if_media.c: In the SIOCGIFMEDIA ioctl in ifmedia_ioctl(), avoid
calling malloc() with M_WAITOK. Return an error if
the M_NOWAIT malloc fails.
The ti(4) driver and the wi(4) driver, at least, call
this with a mutex held. This causes witness warnings
for 'ifconfig -a' with a wi(4) or ti(4) board in the
system. (I've only verified for ti(4)).
ip_output.c: Fragment large datagrams so that each segment contains
a multiple of PAGE_SIZE amount of data plus headers.
This allows the receiver to potentially do page
flipping on receives.
if_ti.c: Add zero copy receive support to the ti(4) driver. If
TI_PRIVATE_JUMBOS is not defined, it now uses the
jumbo(9) buffer allocator for jumbo receive buffers.
Add a new character device interface for the ti(4)
driver for the new debugging interface. This allows
(a patched version of) gdb to talk to the Tigon board
and debug the firmware. There are also a few additional
debugging ioctls available through this interface.
Add header splitting support to the ti(4) driver.
Tweak some of the default interrupt coalescing
parameters to more useful defaults.
Add hooks for supporting transmit flow control, but
leave it turned off with a comment describing why it
is turned off.
if_tireg.h: Change the firmware rev to 12.4.11, since we're really
at 12.4.11 plus fixes from 12.4.13.
Add defines needed for debugging.
Remove the ti_stats structure, it is now defined in
sys/tiio.h.
ti_fw.h: 12.4.11 firmware.
ti_fw2.h: 12.4.11 firmware, plus selected fixes from 12.4.13,
and my header splitting patches. Revision 12.4.13
doesn't handle 10/100 negotiation properly. (This
firmware is the same as what was in the tree previously,
with the addition of header splitting support.)
sys/jumbo.h: Jumbo buffer allocator interface.
sys/mbuf.h: Add a new external mbuf type, EXT_DISPOSABLE, to
indicate that the payload buffer can be thrown away /
flipped to a userland process.
socketvar.h: Add prototype for socow_setup.
tiio.h: ioctl interface to the character portion of the ti(4)
driver, plus associated structure/type definitions.
uio.h: Change prototype for uiomoveco() so that we'll know
whether the source page is disposable.
ufs_readwrite.c:Update for new prototype of uiomoveco().
vm_fault.c: In vm_fault(), check to see whether we need to do a page
based copy on write fault.
vm_object.c: Add a new function, vm_object_allocate_wait(). This
does the same thing that vm_object allocate does, except
that it gives the caller the opportunity to specify whether
it should wait on the uma_zalloc() of the object structre.
This allows vm objects to be allocated while holding a
mutex. (Without generating WITNESS warnings.)
vm_object_allocate() is implemented as a call to
vm_object_allocate_wait() with the malloc flag set to
M_WAITOK.
vm_object.h: Add prototype for vm_object_allocate_wait().
vm_page.c: Add page-based copy on write setup, clear and fault
routines.
vm_page.h: Add page based COW function prototypes and variable in
the vm_page structure.
Many thanks to Drew Gallatin, who wrote the zero copy send and receive
code, and to all the other folks who have tested and reviewed this code
over the years.
2002-06-26 03:37:47 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* firstlen (off - hlen) must be aligned on an
|
|
|
|
* 8-byte boundary
|
|
|
|
*/
|
|
|
|
if (off < hlen)
|
|
|
|
goto smart_frag_failure;
|
|
|
|
off = ((off - hlen) & ~7) + hlen;
|
2003-08-07 18:16:59 +00:00
|
|
|
newlen = (~PAGE_MASK) & mtu;
|
|
|
|
if ((newlen + sizeof (struct ip)) > mtu) {
|
At long last, commit the zero copy sockets code.
MAKEDEV: Add MAKEDEV glue for the ti(4) device nodes.
ti.4: Update the ti(4) man page to include information on the
TI_JUMBO_HDRSPLIT and TI_PRIVATE_JUMBOS kernel options,
and also include information about the new character
device interface and the associated ioctls.
man9/Makefile: Add jumbo.9 and zero_copy.9 man pages and associated
links.
jumbo.9: New man page describing the jumbo buffer allocator
interface and operation.
zero_copy.9: New man page describing the general characteristics of
the zero copy send and receive code, and what an
application author should do to take advantage of the
zero copy functionality.
NOTES: Add entries for ZERO_COPY_SOCKETS, TI_PRIVATE_JUMBOS,
TI_JUMBO_HDRSPLIT, MSIZE, and MCLSHIFT.
conf/files: Add uipc_jumbo.c and uipc_cow.c.
conf/options: Add the 5 options mentioned above.
kern_subr.c: Receive side zero copy implementation. This takes
"disposable" pages attached to an mbuf, gives them to
a user process, and then recycles the user's page.
This is only active when ZERO_COPY_SOCKETS is turned on
and the kern.ipc.zero_copy.receive sysctl variable is
set to 1.
uipc_cow.c: Send side zero copy functions. Takes a page written
by the user and maps it copy on write and assigns it
kernel virtual address space. Removes copy on write
mapping once the buffer has been freed by the network
stack.
uipc_jumbo.c: Jumbo disposable page allocator code. This allocates
(optionally) disposable pages for network drivers that
want to give the user the option of doing zero copy
receive.
uipc_socket.c: Add kern.ipc.zero_copy.{send,receive} sysctls that are
enabled if ZERO_COPY_SOCKETS is turned on.
Add zero copy send support to sosend() -- pages get
mapped into the kernel instead of getting copied if
they meet size and alignment restrictions.
uipc_syscalls.c:Un-staticize some of the sf* functions so that they
can be used elsewhere. (uipc_cow.c)
if_media.c: In the SIOCGIFMEDIA ioctl in ifmedia_ioctl(), avoid
calling malloc() with M_WAITOK. Return an error if
the M_NOWAIT malloc fails.
The ti(4) driver and the wi(4) driver, at least, call
this with a mutex held. This causes witness warnings
for 'ifconfig -a' with a wi(4) or ti(4) board in the
system. (I've only verified for ti(4)).
ip_output.c: Fragment large datagrams so that each segment contains
a multiple of PAGE_SIZE amount of data plus headers.
This allows the receiver to potentially do page
flipping on receives.
if_ti.c: Add zero copy receive support to the ti(4) driver. If
TI_PRIVATE_JUMBOS is not defined, it now uses the
jumbo(9) buffer allocator for jumbo receive buffers.
Add a new character device interface for the ti(4)
driver for the new debugging interface. This allows
(a patched version of) gdb to talk to the Tigon board
and debug the firmware. There are also a few additional
debugging ioctls available through this interface.
Add header splitting support to the ti(4) driver.
Tweak some of the default interrupt coalescing
parameters to more useful defaults.
Add hooks for supporting transmit flow control, but
leave it turned off with a comment describing why it
is turned off.
if_tireg.h: Change the firmware rev to 12.4.11, since we're really
at 12.4.11 plus fixes from 12.4.13.
Add defines needed for debugging.
Remove the ti_stats structure, it is now defined in
sys/tiio.h.
ti_fw.h: 12.4.11 firmware.
ti_fw2.h: 12.4.11 firmware, plus selected fixes from 12.4.13,
and my header splitting patches. Revision 12.4.13
doesn't handle 10/100 negotiation properly. (This
firmware is the same as what was in the tree previously,
with the addition of header splitting support.)
sys/jumbo.h: Jumbo buffer allocator interface.
sys/mbuf.h: Add a new external mbuf type, EXT_DISPOSABLE, to
indicate that the payload buffer can be thrown away /
flipped to a userland process.
socketvar.h: Add prototype for socow_setup.
tiio.h: ioctl interface to the character portion of the ti(4)
driver, plus associated structure/type definitions.
uio.h: Change prototype for uiomoveco() so that we'll know
whether the source page is disposable.
ufs_readwrite.c:Update for new prototype of uiomoveco().
vm_fault.c: In vm_fault(), check to see whether we need to do a page
based copy on write fault.
vm_object.c: Add a new function, vm_object_allocate_wait(). This
does the same thing that vm_object allocate does, except
that it gives the caller the opportunity to specify whether
it should wait on the uma_zalloc() of the object structre.
This allows vm objects to be allocated while holding a
mutex. (Without generating WITNESS warnings.)
vm_object_allocate() is implemented as a call to
vm_object_allocate_wait() with the malloc flag set to
M_WAITOK.
vm_object.h: Add prototype for vm_object_allocate_wait().
vm_page.c: Add page-based copy on write setup, clear and fault
routines.
vm_page.h: Add page based COW function prototypes and variable in
the vm_page structure.
Many thanks to Drew Gallatin, who wrote the zero copy send and receive
code, and to all the other folks who have tested and reviewed this code
over the years.
2002-06-26 03:37:47 +00:00
|
|
|
/* we failed, go back the default */
|
|
|
|
smart_frag_failure:
|
|
|
|
newlen = len;
|
|
|
|
off = hlen + len;
|
|
|
|
}
|
|
|
|
len = newlen;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
off = hlen + len;
|
|
|
|
}
|
|
|
|
|
2003-08-07 18:16:59 +00:00
|
|
|
firstlen = off - hlen;
|
|
|
|
mnext = &m0->m_nextpkt; /* pointer to next packet */
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Loop through length of segment after first fragment,
|
|
|
|
* make new header and copy data of each part and link onto chain.
|
2003-08-07 18:16:59 +00:00
|
|
|
* Here, m0 is the original packet, m is the fragment being created.
|
|
|
|
* The fragments are linked off the m_nextpkt of the original
|
|
|
|
* packet, which after processing serves as the first fragment.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
2003-08-07 18:16:59 +00:00
|
|
|
for (nfrags = 1; off < ip->ip_len; off += len, nfrags++) {
|
|
|
|
struct ip *mhip; /* ip header on the fragment */
|
|
|
|
struct mbuf *m;
|
|
|
|
int mhlen = sizeof (struct ip);
|
|
|
|
|
2003-02-19 05:47:46 +00:00
|
|
|
MGETHDR(m, M_DONTWAIT, MT_HEADER);
|
2004-08-11 10:46:15 +00:00
|
|
|
if (m == NULL) {
|
1994-05-24 10:09:53 +00:00
|
|
|
error = ENOBUFS;
|
|
|
|
ipstat.ips_odropped++;
|
2003-08-07 18:16:59 +00:00
|
|
|
goto done;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
2000-03-27 19:14:27 +00:00
|
|
|
m->m_flags |= (m0->m_flags & M_MCAST) | M_FRAG;
|
2003-08-07 18:16:59 +00:00
|
|
|
/*
|
|
|
|
* In the first mbuf, leave room for the link header, then
|
|
|
|
* copy the original IP header including options. The payload
|
|
|
|
* goes into an additional mbuf chain returned by m_copy().
|
|
|
|
*/
|
1994-05-24 10:09:53 +00:00
|
|
|
m->m_data += max_linkhdr;
|
|
|
|
mhip = mtod(m, struct ip *);
|
|
|
|
*mhip = *ip;
|
|
|
|
if (hlen > sizeof (struct ip)) {
|
|
|
|
mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip);
|
2002-10-20 22:52:07 +00:00
|
|
|
mhip->ip_v = IPVERSION;
|
|
|
|
mhip->ip_hl = mhlen >> 2;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
m->m_len = mhlen;
|
2003-08-07 18:16:59 +00:00
|
|
|
/* XXX do we need to add ip->ip_off below ? */
|
2000-10-20 14:10:37 +00:00
|
|
|
mhip->ip_off = ((off - hlen) >> 3) + ip->ip_off;
|
2003-08-07 18:16:59 +00:00
|
|
|
if (off + len >= ip->ip_len) { /* last fragment */
|
|
|
|
len = ip->ip_len - off;
|
|
|
|
m->m_flags |= M_LASTFRAG;
|
|
|
|
} else
|
1994-05-24 10:09:53 +00:00
|
|
|
mhip->ip_off |= IP_MF;
|
|
|
|
mhip->ip_len = htons((u_short)(len + mhlen));
|
|
|
|
m->m_next = m_copy(m0, off, len);
|
2004-08-11 10:46:15 +00:00
|
|
|
if (m->m_next == NULL) { /* copy failed */
|
2003-08-07 18:16:59 +00:00
|
|
|
m_free(m);
|
1994-05-24 10:09:53 +00:00
|
|
|
error = ENOBUFS; /* ??? */
|
|
|
|
ipstat.ips_odropped++;
|
2003-08-07 18:16:59 +00:00
|
|
|
goto done;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
m->m_pkthdr.len = mhlen + len;
|
2005-06-10 16:49:24 +00:00
|
|
|
m->m_pkthdr.rcvif = NULL;
|
2002-07-31 17:21:01 +00:00
|
|
|
#ifdef MAC
|
|
|
|
mac_create_fragment(m0, m);
|
|
|
|
#endif
|
2000-03-27 19:14:27 +00:00
|
|
|
m->m_pkthdr.csum_flags = m0->m_pkthdr.csum_flags;
|
2002-02-18 20:35:27 +00:00
|
|
|
mhip->ip_off = htons(mhip->ip_off);
|
1994-05-24 10:09:53 +00:00
|
|
|
mhip->ip_sum = 0;
|
2002-10-20 22:52:07 +00:00
|
|
|
if (sw_csum & CSUM_DELAY_IP)
|
|
|
|
mhip->ip_sum = in_cksum(m, mhlen);
|
1994-05-24 10:09:53 +00:00
|
|
|
*mnext = m;
|
|
|
|
mnext = &m->m_nextpkt;
|
|
|
|
}
|
2000-03-27 19:14:27 +00:00
|
|
|
ipstat.ips_ofragments += nfrags;
|
|
|
|
|
2003-08-07 18:16:59 +00:00
|
|
|
/* set first marker for fragment chain */
|
2000-03-27 19:14:27 +00:00
|
|
|
m0->m_flags |= M_FIRSTFRAG | M_FRAG;
|
|
|
|
m0->m_pkthdr.csum_data = nfrags;
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
2002-11-20 18:56:25 +00:00
|
|
|
* Update first fragment by trimming what's been copied out
|
2003-08-07 18:16:59 +00:00
|
|
|
* and updating header.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
2003-08-07 18:16:59 +00:00
|
|
|
m_adj(m0, hlen + firstlen - ip->ip_len);
|
|
|
|
m0->m_pkthdr.len = hlen + firstlen;
|
|
|
|
ip->ip_len = htons((u_short)m0->m_pkthdr.len);
|
Fixed broken ICMP error generation, unified conversion of IP header
fields between host and network byte order. The details:
o icmp_error() now does not add IP header length. This fixes the problem
when icmp_error() is called from ip_forward(). In this case the ip_len
of the original IP datagram returned with ICMP error was wrong.
o icmp_error() expects all three fields, ip_len, ip_id and ip_off in host
byte order, so DTRT and convert these fields back to network byte order
before sending a message. This fixes the problem described in PR 16240
and PR 20877 (ip_id field was returned in host byte order).
o ip_ttl decrement operation in ip_forward() was moved down to make sure
that it does not corrupt the copy of original IP datagram passed later
to icmp_error().
o A copy of original IP datagram in ip_forward() was made a read-write,
independent copy. This fixes the problem I first reported to Garrett
Wollman and Bill Fenner and later put in audit trail of PR 16240:
ip_output() (not always) converts fields of original datagram to network
byte order, but because copy (mcopy) and its original (m) most likely
share the same mbuf cluster, ip_output()'s manipulations on original
also corrupted the copy.
o ip_output() now expects all three fields, ip_len, ip_off and (what is
significant) ip_id in host byte order. It was a headache for years that
ip_id was handled differently. The only compatibility issue here is the
raw IP socket interface with IP_HDRINCL socket option set and a non-zero
ip_id field, but ip.4 manual page was unclear on whether in this case
ip_id field should be in host or network byte order.
2000-09-01 12:33:03 +00:00
|
|
|
ip->ip_off |= IP_MF;
|
2002-02-18 20:35:27 +00:00
|
|
|
ip->ip_off = htons(ip->ip_off);
|
1994-05-24 10:09:53 +00:00
|
|
|
ip->ip_sum = 0;
|
2002-10-20 22:52:07 +00:00
|
|
|
if (sw_csum & CSUM_DELAY_IP)
|
2003-08-07 18:16:59 +00:00
|
|
|
ip->ip_sum = in_cksum(m0, hlen);
|
2002-11-20 18:56:25 +00:00
|
|
|
|
|
|
|
done:
|
2003-08-07 18:16:59 +00:00
|
|
|
*m_frag = m0;
|
|
|
|
return error;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
2000-05-21 21:26:06 +00:00
|
|
|
void
|
2000-03-27 19:14:27 +00:00
|
|
|
in_delayed_cksum(struct mbuf *m)
|
|
|
|
{
|
|
|
|
struct ip *ip;
|
|
|
|
u_short csum, offset;
|
|
|
|
|
|
|
|
ip = mtod(m, struct ip *);
|
2002-10-20 22:52:07 +00:00
|
|
|
offset = ip->ip_hl << 2 ;
|
2000-03-27 19:14:27 +00:00
|
|
|
csum = in_cksum_skip(m, ip->ip_len, offset);
|
RFC768 (UDP) requires that "if the computed checksum is zero, it
is transmitted as all ones". This got broken after introduction
of delayed checksums as follows. Some guys (including Jonathan)
think that it is allowed to transmit all ones in place of a zero
checksum for TCP the same way as for UDP. (The discussion still
takes place on -net.) Thus, the 0 -> 0xffff checksum fixup was
first moved from udp_output() (see udp_usrreq.c, 1.64 -> 1.65)
to in_cksum_skip() (see sys/i386/i386/in_cksum.c, 1.17 -> 1.18,
INVERT expression). Besides that I disagree that it is valid for
TCP, there was no real problem until in_cksum.c,v 1.20, where the
in_cksum() was made just a special version of in_cksum_skip().
The side effect was that now every incoming IP datagram failed to
pass the checksum test (in_cksum() returned 0xffff when it should
actually return zero). It was fixed next day in revision 1.21,
by removing the INVERT expression. The latter also broke the
0 -> 0xffff fixup for UDP checksums.
Before this change:
: tcpdump: listening on lo0
: 127.0.0.1.33005 > 127.0.0.1.33006: udp 0 (ttl 64, id 1)
: 4500 001c 0001 0000 4011 7cce 7f00 0001
: 7f00 0001 80ed 80ee 0008 0000
After this change:
: tcpdump: listening on lo0
: 127.0.0.1.33005 > 127.0.0.1.33006: udp 0 (ttl 64, id 1)
: 4500 001c 0001 0000 4011 7cce 7f00 0001
: 7f00 0001 80ed 80ee 0008 ffff
2001-03-13 17:07:06 +00:00
|
|
|
if (m->m_pkthdr.csum_flags & CSUM_UDP && csum == 0)
|
|
|
|
csum = 0xffff;
|
2000-03-27 19:14:27 +00:00
|
|
|
offset += m->m_pkthdr.csum_data; /* checksum offset */
|
|
|
|
|
|
|
|
if (offset + sizeof(u_short) > m->m_len) {
|
|
|
|
printf("delayed m_pullup, m->len: %d off: %d p: %d\n",
|
|
|
|
m->m_len, offset, ip->ip_p);
|
|
|
|
/*
|
|
|
|
* XXX
|
|
|
|
* this shouldn't happen, but if it does, the
|
|
|
|
* correct behavior may be to insert the checksum
|
|
|
|
* in the existing chain instead of rearranging it.
|
|
|
|
*/
|
|
|
|
m = m_pullup(m, offset + sizeof(u_short));
|
|
|
|
}
|
|
|
|
*(u_short *)(m->m_data + offset) = csum;
|
|
|
|
}
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* Insert IP options into preformed packet.
|
|
|
|
* Adjust IP destination as required for IP source routing,
|
|
|
|
* as indicated by a non-zero in_addr at the start of the options.
|
1996-03-13 08:02:45 +00:00
|
|
|
*
|
|
|
|
* XXX This routine assumes that the packet has no options in place.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
|
|
|
static struct mbuf *
|
2002-11-20 18:56:25 +00:00
|
|
|
ip_insertoptions(m, opt, phlen)
|
|
|
|
register struct mbuf *m;
|
|
|
|
struct mbuf *opt;
|
|
|
|
int *phlen;
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2002-11-20 18:56:25 +00:00
|
|
|
register struct ipoption *p = mtod(opt, struct ipoption *);
|
1994-05-24 10:09:53 +00:00
|
|
|
struct mbuf *n;
|
2002-11-20 18:56:25 +00:00
|
|
|
register struct ip *ip = mtod(m, struct ip *);
|
1994-05-24 10:09:53 +00:00
|
|
|
unsigned optlen;
|
|
|
|
|
|
|
|
optlen = opt->m_len - sizeof(p->ipopt_dst);
|
2003-08-07 18:16:59 +00:00
|
|
|
if (optlen + ip->ip_len > IP_MAXPACKET) {
|
2002-09-23 08:56:24 +00:00
|
|
|
*phlen = 0;
|
1994-05-24 10:09:53 +00:00
|
|
|
return (m); /* XXX should fail */
|
2002-09-23 08:56:24 +00:00
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
if (p->ipopt_dst.s_addr)
|
|
|
|
ip->ip_dst = p->ipopt_dst;
|
|
|
|
if (m->m_flags & M_EXT || m->m_data - optlen < m->m_pktdat) {
|
2003-02-19 05:47:46 +00:00
|
|
|
MGETHDR(n, M_DONTWAIT, MT_HEADER);
|
2004-08-11 10:46:15 +00:00
|
|
|
if (n == NULL) {
|
2002-09-23 08:56:24 +00:00
|
|
|
*phlen = 0;
|
1994-05-24 10:09:53 +00:00
|
|
|
return (m);
|
2002-09-23 08:56:24 +00:00
|
|
|
}
|
2005-01-23 19:43:46 +00:00
|
|
|
M_MOVE_PKTHDR(n, m);
|
2005-06-10 16:49:24 +00:00
|
|
|
n->m_pkthdr.rcvif = NULL;
|
2002-07-31 17:21:01 +00:00
|
|
|
#ifdef MAC
|
2005-07-05 23:39:51 +00:00
|
|
|
mac_copy_mbuf(m, n);
|
2002-07-31 17:21:01 +00:00
|
|
|
#endif
|
2005-01-23 19:43:46 +00:00
|
|
|
n->m_pkthdr.len += optlen;
|
1994-05-24 10:09:53 +00:00
|
|
|
m->m_len -= sizeof(struct ip);
|
|
|
|
m->m_data += sizeof(struct ip);
|
|
|
|
n->m_next = m;
|
|
|
|
m = n;
|
|
|
|
m->m_len = optlen + sizeof(struct ip);
|
|
|
|
m->m_data += max_linkhdr;
|
2003-04-04 12:14:00 +00:00
|
|
|
bcopy(ip, mtod(m, void *), sizeof(struct ip));
|
1994-05-24 10:09:53 +00:00
|
|
|
} else {
|
|
|
|
m->m_data -= optlen;
|
|
|
|
m->m_len += optlen;
|
|
|
|
m->m_pkthdr.len += optlen;
|
2003-04-04 12:14:00 +00:00
|
|
|
bcopy(ip, mtod(m, void *), sizeof(struct ip));
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
ip = mtod(m, struct ip *);
|
1996-06-08 08:19:03 +00:00
|
|
|
bcopy(p->ipopt_list, ip + 1, optlen);
|
1994-05-24 10:09:53 +00:00
|
|
|
*phlen = sizeof(struct ip) + optlen;
|
2002-10-20 22:52:07 +00:00
|
|
|
ip->ip_v = IPVERSION;
|
|
|
|
ip->ip_hl = *phlen >> 2;
|
1994-05-24 10:09:53 +00:00
|
|
|
ip->ip_len += optlen;
|
|
|
|
return (m);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy options from ip to jp,
|
|
|
|
* omitting those not copied during fragmentation.
|
|
|
|
*/
|
1997-04-03 10:47:12 +00:00
|
|
|
int
|
2002-11-20 18:56:25 +00:00
|
|
|
ip_optcopy(ip, jp)
|
|
|
|
struct ip *ip, *jp;
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2002-11-20 18:56:25 +00:00
|
|
|
register u_char *cp, *dp;
|
1994-05-24 10:09:53 +00:00
|
|
|
int opt, optlen, cnt;
|
|
|
|
|
|
|
|
cp = (u_char *)(ip + 1);
|
|
|
|
dp = (u_char *)(jp + 1);
|
2002-10-20 22:52:07 +00:00
|
|
|
cnt = (ip->ip_hl << 2) - sizeof (struct ip);
|
1994-05-24 10:09:53 +00:00
|
|
|
for (; cnt > 0; cnt -= optlen, cp += optlen) {
|
|
|
|
opt = cp[0];
|
|
|
|
if (opt == IPOPT_EOL)
|
|
|
|
break;
|
|
|
|
if (opt == IPOPT_NOP) {
|
|
|
|
/* Preserve for IP mcast tunnel's LSRR alignment. */
|
|
|
|
*dp++ = IPOPT_NOP;
|
|
|
|
optlen = 1;
|
|
|
|
continue;
|
2000-07-04 16:35:15 +00:00
|
|
|
}
|
2002-05-21 18:52:24 +00:00
|
|
|
|
|
|
|
KASSERT(cnt >= IPOPT_OLEN + sizeof(*cp),
|
|
|
|
("ip_optcopy: malformed ipv4 option"));
|
2000-07-04 16:35:15 +00:00
|
|
|
optlen = cp[IPOPT_OLEN];
|
2002-05-21 18:52:24 +00:00
|
|
|
KASSERT(optlen >= IPOPT_OLEN + sizeof(*cp) && optlen <= cnt,
|
|
|
|
("ip_optcopy: malformed ipv4 option"));
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/* bogus lengths should have been caught by ip_dooptions */
|
|
|
|
if (optlen > cnt)
|
|
|
|
optlen = cnt;
|
|
|
|
if (IPOPT_COPIED(opt)) {
|
1996-06-08 08:19:03 +00:00
|
|
|
bcopy(cp, dp, optlen);
|
1994-05-24 10:09:53 +00:00
|
|
|
dp += optlen;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++)
|
|
|
|
*dp++ = IPOPT_EOL;
|
|
|
|
return (optlen);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* IP socket option processing.
|
|
|
|
*/
|
|
|
|
int
|
2002-11-20 18:56:25 +00:00
|
|
|
ip_ctloutput(so, sopt)
|
|
|
|
struct socket *so;
|
|
|
|
struct sockopt *sopt;
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
1998-08-23 03:07:17 +00:00
|
|
|
struct inpcb *inp = sotoinpcb(so);
|
|
|
|
int error, optval;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1998-08-23 03:07:17 +00:00
|
|
|
error = optval = 0;
|
|
|
|
if (sopt->sopt_level != IPPROTO_IP) {
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1998-08-23 03:07:17 +00:00
|
|
|
switch (sopt->sopt_dir) {
|
|
|
|
case SOPT_SET:
|
|
|
|
switch (sopt->sopt_name) {
|
1994-05-24 10:09:53 +00:00
|
|
|
case IP_OPTIONS:
|
|
|
|
#ifdef notyet
|
|
|
|
case IP_RETOPTS:
|
|
|
|
#endif
|
1998-08-23 03:07:17 +00:00
|
|
|
{
|
|
|
|
struct mbuf *m;
|
|
|
|
if (sopt->sopt_valsize > MLEN) {
|
|
|
|
error = EMSGSIZE;
|
|
|
|
break;
|
|
|
|
}
|
2003-02-19 05:47:46 +00:00
|
|
|
MGET(m, sopt->sopt_td ? M_TRYWAIT : M_DONTWAIT, MT_HEADER);
|
2004-08-11 10:46:15 +00:00
|
|
|
if (m == NULL) {
|
1998-08-23 03:07:17 +00:00
|
|
|
error = ENOBUFS;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
m->m_len = sopt->sopt_valsize;
|
|
|
|
error = sooptcopyin(sopt, mtod(m, char *), m->m_len,
|
|
|
|
m->m_len);
|
2004-12-05 19:11:09 +00:00
|
|
|
INP_LOCK(inp);
|
|
|
|
error = ip_pcbopts(inp, sopt->sopt_name, m);
|
|
|
|
INP_UNLOCK(inp);
|
|
|
|
return (error);
|
1998-08-23 03:07:17 +00:00
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
case IP_TOS:
|
|
|
|
case IP_TTL:
|
2005-08-22 16:13:08 +00:00
|
|
|
case IP_MINTTL:
|
1994-05-24 10:09:53 +00:00
|
|
|
case IP_RECVOPTS:
|
|
|
|
case IP_RECVRETOPTS:
|
|
|
|
case IP_RECVDSTADDR:
|
2003-04-29 21:36:18 +00:00
|
|
|
case IP_RECVTTL:
|
1996-11-11 04:56:32 +00:00
|
|
|
case IP_RECVIF:
|
1999-12-22 19:13:38 +00:00
|
|
|
case IP_FAITH:
|
2003-08-20 14:46:40 +00:00
|
|
|
case IP_ONESBCAST:
|
1998-08-23 03:07:17 +00:00
|
|
|
error = sooptcopyin(sopt, &optval, sizeof optval,
|
|
|
|
sizeof optval);
|
|
|
|
if (error)
|
|
|
|
break;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1998-08-23 03:07:17 +00:00
|
|
|
switch (sopt->sopt_name) {
|
|
|
|
case IP_TOS:
|
|
|
|
inp->inp_ip_tos = optval;
|
|
|
|
break;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1998-08-23 03:07:17 +00:00
|
|
|
case IP_TTL:
|
|
|
|
inp->inp_ip_ttl = optval;
|
|
|
|
break;
|
2005-08-22 16:13:08 +00:00
|
|
|
|
|
|
|
case IP_MINTTL:
|
|
|
|
if (optval > 0 && optval <= MAXTTL)
|
|
|
|
inp->inp_ip_minttl = optval;
|
|
|
|
else
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
|
2004-06-24 02:05:47 +00:00
|
|
|
#define OPTSET(bit) do { \
|
|
|
|
INP_LOCK(inp); \
|
|
|
|
if (optval) \
|
|
|
|
inp->inp_flags |= bit; \
|
|
|
|
else \
|
|
|
|
inp->inp_flags &= ~bit; \
|
|
|
|
INP_UNLOCK(inp); \
|
|
|
|
} while (0)
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1998-08-23 03:07:17 +00:00
|
|
|
case IP_RECVOPTS:
|
|
|
|
OPTSET(INP_RECVOPTS);
|
|
|
|
break;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1998-08-23 03:07:17 +00:00
|
|
|
case IP_RECVRETOPTS:
|
|
|
|
OPTSET(INP_RECVRETOPTS);
|
|
|
|
break;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1998-08-23 03:07:17 +00:00
|
|
|
case IP_RECVDSTADDR:
|
|
|
|
OPTSET(INP_RECVDSTADDR);
|
|
|
|
break;
|
1996-11-11 04:56:32 +00:00
|
|
|
|
2003-04-29 21:36:18 +00:00
|
|
|
case IP_RECVTTL:
|
|
|
|
OPTSET(INP_RECVTTL);
|
|
|
|
break;
|
|
|
|
|
1998-08-23 03:07:17 +00:00
|
|
|
case IP_RECVIF:
|
|
|
|
OPTSET(INP_RECVIF);
|
|
|
|
break;
|
1999-12-22 19:13:38 +00:00
|
|
|
|
|
|
|
case IP_FAITH:
|
|
|
|
OPTSET(INP_FAITH);
|
|
|
|
break;
|
2003-08-20 14:46:40 +00:00
|
|
|
|
|
|
|
case IP_ONESBCAST:
|
|
|
|
OPTSET(INP_ONESBCAST);
|
|
|
|
break;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
#undef OPTSET
|
|
|
|
|
|
|
|
case IP_MULTICAST_IF:
|
1994-09-06 22:42:31 +00:00
|
|
|
case IP_MULTICAST_VIF:
|
1994-05-24 10:09:53 +00:00
|
|
|
case IP_MULTICAST_TTL:
|
|
|
|
case IP_MULTICAST_LOOP:
|
|
|
|
case IP_ADD_MEMBERSHIP:
|
|
|
|
case IP_DROP_MEMBERSHIP:
|
2004-12-05 21:38:33 +00:00
|
|
|
error = ip_setmoptions(inp, sopt);
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
|
1996-02-22 21:32:23 +00:00
|
|
|
case IP_PORTRANGE:
|
1998-08-23 03:07:17 +00:00
|
|
|
error = sooptcopyin(sopt, &optval, sizeof optval,
|
|
|
|
sizeof optval);
|
|
|
|
if (error)
|
|
|
|
break;
|
1996-02-22 21:32:23 +00:00
|
|
|
|
2004-06-24 02:05:47 +00:00
|
|
|
INP_LOCK(inp);
|
1998-08-23 03:07:17 +00:00
|
|
|
switch (optval) {
|
|
|
|
case IP_PORTRANGE_DEFAULT:
|
|
|
|
inp->inp_flags &= ~(INP_LOWPORT);
|
|
|
|
inp->inp_flags &= ~(INP_HIGHPORT);
|
|
|
|
break;
|
1996-02-22 21:32:23 +00:00
|
|
|
|
1998-08-23 03:07:17 +00:00
|
|
|
case IP_PORTRANGE_HIGH:
|
|
|
|
inp->inp_flags &= ~(INP_LOWPORT);
|
|
|
|
inp->inp_flags |= INP_HIGHPORT;
|
|
|
|
break;
|
1996-02-22 21:32:23 +00:00
|
|
|
|
1998-08-23 03:07:17 +00:00
|
|
|
case IP_PORTRANGE_LOW:
|
|
|
|
inp->inp_flags &= ~(INP_HIGHPORT);
|
|
|
|
inp->inp_flags |= INP_LOWPORT;
|
|
|
|
break;
|
1996-02-22 21:32:23 +00:00
|
|
|
|
1998-08-23 03:07:17 +00:00
|
|
|
default:
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
1996-02-22 21:32:23 +00:00
|
|
|
}
|
2004-06-24 02:05:47 +00:00
|
|
|
INP_UNLOCK(inp);
|
1996-05-21 20:47:31 +00:00
|
|
|
break;
|
1996-02-22 21:32:23 +00:00
|
|
|
|
2002-10-16 02:25:05 +00:00
|
|
|
#if defined(IPSEC) || defined(FAST_IPSEC)
|
1999-12-22 19:13:38 +00:00
|
|
|
case IP_IPSEC_POLICY:
|
|
|
|
{
|
|
|
|
caddr_t req;
|
2000-07-04 16:35:15 +00:00
|
|
|
size_t len = 0;
|
1999-12-22 19:13:38 +00:00
|
|
|
int priv;
|
|
|
|
struct mbuf *m;
|
|
|
|
int optname;
|
|
|
|
|
|
|
|
if ((error = soopt_getm(sopt, &m)) != 0) /* XXX */
|
|
|
|
break;
|
|
|
|
if ((error = soopt_mcopyin(sopt, m)) != 0) /* XXX */
|
|
|
|
break;
|
2001-09-12 08:38:13 +00:00
|
|
|
priv = (sopt->sopt_td != NULL &&
|
2002-04-01 21:31:13 +00:00
|
|
|
suser(sopt->sopt_td) != 0) ? 0 : 1;
|
1999-12-22 19:13:38 +00:00
|
|
|
req = mtod(m, caddr_t);
|
2000-07-04 16:35:15 +00:00
|
|
|
len = m->m_len;
|
1999-12-22 19:13:38 +00:00
|
|
|
optname = sopt->sopt_name;
|
2000-07-04 16:35:15 +00:00
|
|
|
error = ipsec4_set_policy(inp, optname, req, len, priv);
|
1999-12-22 19:13:38 +00:00
|
|
|
m_freem(m);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif /*IPSEC*/
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
default:
|
|
|
|
error = ENOPROTOOPT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
1998-08-23 03:07:17 +00:00
|
|
|
case SOPT_GET:
|
|
|
|
switch (sopt->sopt_name) {
|
1994-05-24 10:09:53 +00:00
|
|
|
case IP_OPTIONS:
|
|
|
|
case IP_RETOPTS:
|
1998-08-23 03:07:17 +00:00
|
|
|
if (inp->inp_options)
|
|
|
|
error = sooptcopyout(sopt,
|
|
|
|
mtod(inp->inp_options,
|
|
|
|
char *),
|
|
|
|
inp->inp_options->m_len);
|
|
|
|
else
|
|
|
|
sopt->sopt_valsize = 0;
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case IP_TOS:
|
|
|
|
case IP_TTL:
|
2005-08-22 16:13:08 +00:00
|
|
|
case IP_MINTTL:
|
1994-05-24 10:09:53 +00:00
|
|
|
case IP_RECVOPTS:
|
|
|
|
case IP_RECVRETOPTS:
|
|
|
|
case IP_RECVDSTADDR:
|
2003-04-29 21:36:18 +00:00
|
|
|
case IP_RECVTTL:
|
1996-11-11 04:56:32 +00:00
|
|
|
case IP_RECVIF:
|
1998-08-23 03:07:17 +00:00
|
|
|
case IP_PORTRANGE:
|
1999-12-22 19:13:38 +00:00
|
|
|
case IP_FAITH:
|
2003-08-20 14:46:40 +00:00
|
|
|
case IP_ONESBCAST:
|
1998-08-23 03:07:17 +00:00
|
|
|
switch (sopt->sopt_name) {
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
case IP_TOS:
|
1997-04-03 05:14:45 +00:00
|
|
|
optval = inp->inp_ip_tos;
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case IP_TTL:
|
1997-04-03 05:14:45 +00:00
|
|
|
optval = inp->inp_ip_ttl;
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
|
2005-08-22 16:13:08 +00:00
|
|
|
case IP_MINTTL:
|
|
|
|
optval = inp->inp_ip_minttl;
|
|
|
|
break;
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
#define OPTBIT(bit) (inp->inp_flags & bit ? 1 : 0)
|
|
|
|
|
|
|
|
case IP_RECVOPTS:
|
|
|
|
optval = OPTBIT(INP_RECVOPTS);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IP_RECVRETOPTS:
|
|
|
|
optval = OPTBIT(INP_RECVRETOPTS);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IP_RECVDSTADDR:
|
|
|
|
optval = OPTBIT(INP_RECVDSTADDR);
|
|
|
|
break;
|
1996-11-11 04:56:32 +00:00
|
|
|
|
2003-04-29 21:36:18 +00:00
|
|
|
case IP_RECVTTL:
|
|
|
|
optval = OPTBIT(INP_RECVTTL);
|
|
|
|
break;
|
|
|
|
|
1996-11-11 04:56:32 +00:00
|
|
|
case IP_RECVIF:
|
|
|
|
optval = OPTBIT(INP_RECVIF);
|
|
|
|
break;
|
1998-08-23 03:07:17 +00:00
|
|
|
|
|
|
|
case IP_PORTRANGE:
|
|
|
|
if (inp->inp_flags & INP_HIGHPORT)
|
|
|
|
optval = IP_PORTRANGE_HIGH;
|
|
|
|
else if (inp->inp_flags & INP_LOWPORT)
|
|
|
|
optval = IP_PORTRANGE_LOW;
|
|
|
|
else
|
|
|
|
optval = 0;
|
|
|
|
break;
|
1999-12-22 19:13:38 +00:00
|
|
|
|
|
|
|
case IP_FAITH:
|
|
|
|
optval = OPTBIT(INP_FAITH);
|
|
|
|
break;
|
2003-08-20 14:46:40 +00:00
|
|
|
|
|
|
|
case IP_ONESBCAST:
|
|
|
|
optval = OPTBIT(INP_ONESBCAST);
|
|
|
|
break;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
1998-08-23 03:07:17 +00:00
|
|
|
error = sooptcopyout(sopt, &optval, sizeof optval);
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case IP_MULTICAST_IF:
|
1994-09-06 22:42:31 +00:00
|
|
|
case IP_MULTICAST_VIF:
|
1994-05-24 10:09:53 +00:00
|
|
|
case IP_MULTICAST_TTL:
|
|
|
|
case IP_MULTICAST_LOOP:
|
|
|
|
case IP_ADD_MEMBERSHIP:
|
|
|
|
case IP_DROP_MEMBERSHIP:
|
2004-12-05 22:08:37 +00:00
|
|
|
error = ip_getmoptions(inp, sopt);
|
1996-02-22 21:32:23 +00:00
|
|
|
break;
|
|
|
|
|
2002-10-16 02:25:05 +00:00
|
|
|
#if defined(IPSEC) || defined(FAST_IPSEC)
|
1999-12-22 19:13:38 +00:00
|
|
|
case IP_IPSEC_POLICY:
|
|
|
|
{
|
2000-03-09 14:57:16 +00:00
|
|
|
struct mbuf *m = NULL;
|
1999-12-22 19:13:38 +00:00
|
|
|
caddr_t req = NULL;
|
2000-07-04 16:35:15 +00:00
|
|
|
size_t len = 0;
|
1999-12-22 19:13:38 +00:00
|
|
|
|
2000-07-04 16:35:15 +00:00
|
|
|
if (m != 0) {
|
1999-12-22 19:13:38 +00:00
|
|
|
req = mtod(m, caddr_t);
|
2000-07-04 16:35:15 +00:00
|
|
|
len = m->m_len;
|
|
|
|
}
|
|
|
|
error = ipsec4_get_policy(sotoinpcb(so), req, len, &m);
|
1999-12-22 19:13:38 +00:00
|
|
|
if (error == 0)
|
|
|
|
error = soopt_mcopyout(sopt, m); /* XXX */
|
2000-03-09 14:57:16 +00:00
|
|
|
if (error == 0)
|
|
|
|
m_freem(m);
|
1999-12-22 19:13:38 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif /*IPSEC*/
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
default:
|
|
|
|
error = ENOPROTOOPT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up IP options in pcb for insertion in output packets.
|
|
|
|
* Store in mbuf with pointer in pcbopt, adding pseudo-option
|
|
|
|
* with destination address if source routed.
|
|
|
|
*/
|
1995-11-14 20:34:56 +00:00
|
|
|
static int
|
2004-12-05 19:11:09 +00:00
|
|
|
ip_pcbopts(struct inpcb *inp, int optname, struct mbuf *m)
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2002-11-20 18:56:25 +00:00
|
|
|
register int cnt, optlen;
|
|
|
|
register u_char *cp;
|
2004-12-05 19:11:09 +00:00
|
|
|
struct mbuf **pcbopt;
|
1994-05-24 10:09:53 +00:00
|
|
|
u_char opt;
|
|
|
|
|
2004-12-05 19:11:09 +00:00
|
|
|
INP_LOCK_ASSERT(inp);
|
|
|
|
|
|
|
|
pcbopt = &inp->inp_options;
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/* turn off any old options */
|
|
|
|
if (*pcbopt)
|
|
|
|
(void)m_free(*pcbopt);
|
|
|
|
*pcbopt = 0;
|
2004-12-05 19:11:09 +00:00
|
|
|
if (m == NULL || m->m_len == 0) {
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* Only turning off any previous options.
|
|
|
|
*/
|
2004-12-05 19:11:09 +00:00
|
|
|
if (m != NULL)
|
1994-05-24 10:09:53 +00:00
|
|
|
(void)m_free(m);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1998-07-13 12:12:25 +00:00
|
|
|
if (m->m_len % sizeof(int32_t))
|
1994-05-24 10:09:53 +00:00
|
|
|
goto bad;
|
|
|
|
/*
|
|
|
|
* IP first-hop destination address will be stored before
|
|
|
|
* actual options; move other options back
|
|
|
|
* and clear it when none present.
|
|
|
|
*/
|
|
|
|
if (m->m_data + m->m_len + sizeof(struct in_addr) >= &m->m_dat[MLEN])
|
|
|
|
goto bad;
|
|
|
|
cnt = m->m_len;
|
|
|
|
m->m_len += sizeof(struct in_addr);
|
|
|
|
cp = mtod(m, u_char *) + sizeof(struct in_addr);
|
2003-04-04 12:14:00 +00:00
|
|
|
bcopy(mtod(m, void *), cp, (unsigned)cnt);
|
|
|
|
bzero(mtod(m, void *), sizeof(struct in_addr));
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
for (; cnt > 0; cnt -= optlen, cp += optlen) {
|
|
|
|
opt = cp[IPOPT_OPTVAL];
|
|
|
|
if (opt == IPOPT_EOL)
|
|
|
|
break;
|
|
|
|
if (opt == IPOPT_NOP)
|
|
|
|
optlen = 1;
|
|
|
|
else {
|
2000-06-02 20:18:38 +00:00
|
|
|
if (cnt < IPOPT_OLEN + sizeof(*cp))
|
|
|
|
goto bad;
|
1994-05-24 10:09:53 +00:00
|
|
|
optlen = cp[IPOPT_OLEN];
|
2000-06-02 20:18:38 +00:00
|
|
|
if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt)
|
1994-05-24 10:09:53 +00:00
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
switch (opt) {
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPOPT_LSRR:
|
|
|
|
case IPOPT_SSRR:
|
|
|
|
/*
|
|
|
|
* user process specifies route as:
|
|
|
|
* ->A->B->C->D
|
|
|
|
* D must be our final destination (but we can't
|
|
|
|
* check that since we may not have connected yet).
|
|
|
|
* A is first hop destination, which doesn't appear in
|
|
|
|
* actual IP option, but is stored before the options.
|
|
|
|
*/
|
|
|
|
if (optlen < IPOPT_MINOFF - 1 + sizeof(struct in_addr))
|
|
|
|
goto bad;
|
|
|
|
m->m_len -= sizeof(struct in_addr);
|
|
|
|
cnt -= sizeof(struct in_addr);
|
|
|
|
optlen -= sizeof(struct in_addr);
|
|
|
|
cp[IPOPT_OLEN] = optlen;
|
|
|
|
/*
|
|
|
|
* Move first hop before start of options.
|
|
|
|
*/
|
|
|
|
bcopy((caddr_t)&cp[IPOPT_OFFSET+1], mtod(m, caddr_t),
|
|
|
|
sizeof(struct in_addr));
|
|
|
|
/*
|
|
|
|
* Then copy rest of options back
|
|
|
|
* to close up the deleted entry.
|
|
|
|
*/
|
2003-04-04 12:14:00 +00:00
|
|
|
bcopy((&cp[IPOPT_OFFSET+1] + sizeof(struct in_addr)),
|
|
|
|
&cp[IPOPT_OFFSET+1],
|
2004-05-11 19:14:44 +00:00
|
|
|
(unsigned)cnt - (IPOPT_MINOFF - 1));
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (m->m_len > MAX_IPOPTLEN + sizeof(struct in_addr))
|
|
|
|
goto bad;
|
|
|
|
*pcbopt = m;
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
bad:
|
|
|
|
(void)m_free(m);
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
|
1998-08-23 03:07:17 +00:00
|
|
|
/*
|
|
|
|
* XXX
|
|
|
|
* The whole multicast option thing needs to be re-thought.
|
|
|
|
* Several of these options are equally applicable to non-multicast
|
|
|
|
* transmission, and one (IP_MULTICAST_TTL) totally duplicates a
|
|
|
|
* standard option (IP_TTL).
|
|
|
|
*/
|
2001-06-11 12:39:29 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* following RFC1724 section 3.3, 0.0.0.0/8 is interpreted as interface index.
|
|
|
|
*/
|
|
|
|
static struct ifnet *
|
2002-11-20 18:56:25 +00:00
|
|
|
ip_multicast_if(a, ifindexp)
|
|
|
|
struct in_addr *a;
|
|
|
|
int *ifindexp;
|
2001-06-11 12:39:29 +00:00
|
|
|
{
|
|
|
|
int ifindex;
|
|
|
|
struct ifnet *ifp;
|
|
|
|
|
|
|
|
if (ifindexp)
|
|
|
|
*ifindexp = 0;
|
|
|
|
if (ntohl(a->s_addr) >> 24 == 0) {
|
|
|
|
ifindex = ntohl(a->s_addr) & 0xffffff;
|
|
|
|
if (ifindex < 0 || if_index < ifindex)
|
|
|
|
return NULL;
|
2001-09-06 02:40:43 +00:00
|
|
|
ifp = ifnet_byindex(ifindex);
|
2001-06-11 12:39:29 +00:00
|
|
|
if (ifindexp)
|
|
|
|
*ifindexp = ifindex;
|
|
|
|
} else {
|
|
|
|
INADDR_TO_IFP(*a, ifp);
|
|
|
|
}
|
|
|
|
return ifp;
|
|
|
|
}
|
|
|
|
|
2005-08-09 17:19:21 +00:00
|
|
|
/*
|
|
|
|
* Given an inpcb, return its multicast options structure pointer. Accepts
|
|
|
|
* an unlocked inpcb pointer, but will return it locked. May sleep.
|
|
|
|
*/
|
|
|
|
static struct ip_moptions *
|
|
|
|
ip_findmoptions(struct inpcb *inp)
|
|
|
|
{
|
|
|
|
struct ip_moptions *imo;
|
|
|
|
|
|
|
|
INP_LOCK(inp);
|
|
|
|
if (inp->inp_moptions != NULL)
|
|
|
|
return (inp->inp_moptions);
|
|
|
|
|
|
|
|
INP_UNLOCK(inp);
|
|
|
|
|
|
|
|
imo = (struct ip_moptions*)malloc(sizeof(*imo), M_IPMOPTS, M_WAITOK);
|
|
|
|
|
|
|
|
imo->imo_multicast_ifp = NULL;
|
|
|
|
imo->imo_multicast_addr.s_addr = INADDR_ANY;
|
|
|
|
imo->imo_multicast_vif = -1;
|
|
|
|
imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
|
|
|
|
imo->imo_multicast_loop = IP_DEFAULT_MULTICAST_LOOP;
|
|
|
|
imo->imo_num_memberships = 0;
|
|
|
|
|
|
|
|
INP_LOCK(inp);
|
|
|
|
if (inp->inp_moptions != NULL) {
|
|
|
|
free(imo, M_IPMOPTS);
|
|
|
|
return (inp->inp_moptions);
|
|
|
|
}
|
|
|
|
inp->inp_moptions = imo;
|
|
|
|
return (imo);
|
|
|
|
}
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* Set the IP multicast options in response to user setsockopt().
|
|
|
|
*/
|
1995-11-14 20:34:56 +00:00
|
|
|
static int
|
2004-12-05 21:38:33 +00:00
|
|
|
ip_setmoptions(struct inpcb *inp, struct sockopt *sopt)
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
1998-08-23 03:07:17 +00:00
|
|
|
int error = 0;
|
|
|
|
int i;
|
1994-05-24 10:09:53 +00:00
|
|
|
struct in_addr addr;
|
1998-08-23 03:07:17 +00:00
|
|
|
struct ip_mreq mreq;
|
|
|
|
struct ifnet *ifp;
|
2004-12-05 21:38:33 +00:00
|
|
|
struct ip_moptions *imo;
|
1994-05-24 10:09:53 +00:00
|
|
|
struct route ro;
|
1998-08-23 03:07:17 +00:00
|
|
|
struct sockaddr_in *dst;
|
2001-06-11 12:39:29 +00:00
|
|
|
int ifindex;
|
1995-03-20 18:11:31 +00:00
|
|
|
int s;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1998-08-23 03:07:17 +00:00
|
|
|
switch (sopt->sopt_name) {
|
1994-09-06 22:42:31 +00:00
|
|
|
/* store an index number for the vif you wanna use in the send */
|
|
|
|
case IP_MULTICAST_VIF:
|
1998-08-23 03:07:17 +00:00
|
|
|
if (legal_vif_num == 0) {
|
1994-09-14 03:10:15 +00:00
|
|
|
error = EOPNOTSUPP;
|
|
|
|
break;
|
|
|
|
}
|
1998-08-23 03:07:17 +00:00
|
|
|
error = sooptcopyin(sopt, &i, sizeof i, sizeof i);
|
|
|
|
if (error)
|
1994-09-06 22:42:31 +00:00
|
|
|
break;
|
1995-06-13 17:51:16 +00:00
|
|
|
if (!legal_vif_num(i) && (i != -1)) {
|
1994-09-06 22:42:31 +00:00
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
2005-08-09 17:19:21 +00:00
|
|
|
imo = ip_findmoptions(inp);
|
1994-09-06 22:42:31 +00:00
|
|
|
imo->imo_multicast_vif = i;
|
2005-08-09 17:19:21 +00:00
|
|
|
INP_UNLOCK(inp);
|
1994-09-06 22:42:31 +00:00
|
|
|
break;
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
case IP_MULTICAST_IF:
|
|
|
|
/*
|
|
|
|
* Select the interface for outgoing multicast packets.
|
|
|
|
*/
|
1998-08-23 03:07:17 +00:00
|
|
|
error = sooptcopyin(sopt, &addr, sizeof addr, sizeof addr);
|
|
|
|
if (error)
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
/*
|
|
|
|
* INADDR_ANY is used to remove a previous selection.
|
|
|
|
* When no interface is selected, a default one is
|
|
|
|
* chosen every time a multicast packet is sent.
|
|
|
|
*/
|
2005-08-09 17:19:21 +00:00
|
|
|
imo = ip_findmoptions(inp);
|
1994-05-24 10:09:53 +00:00
|
|
|
if (addr.s_addr == INADDR_ANY) {
|
|
|
|
imo->imo_multicast_ifp = NULL;
|
2005-08-09 17:19:21 +00:00
|
|
|
INP_UNLOCK(inp);
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* The selected interface is identified by its local
|
|
|
|
* IP address. Find the interface and confirm that
|
|
|
|
* it supports multicasting.
|
|
|
|
*/
|
1995-03-20 18:31:51 +00:00
|
|
|
s = splimp();
|
2001-06-11 12:39:29 +00:00
|
|
|
ifp = ip_multicast_if(&addr, &ifindex);
|
1994-05-24 10:09:53 +00:00
|
|
|
if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
|
2005-08-09 17:19:21 +00:00
|
|
|
INP_UNLOCK(inp);
|
1996-03-26 18:56:51 +00:00
|
|
|
splx(s);
|
1994-05-24 10:09:53 +00:00
|
|
|
error = EADDRNOTAVAIL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
imo->imo_multicast_ifp = ifp;
|
2001-06-11 12:39:29 +00:00
|
|
|
if (ifindex)
|
|
|
|
imo->imo_multicast_addr = addr;
|
|
|
|
else
|
|
|
|
imo->imo_multicast_addr.s_addr = INADDR_ANY;
|
2005-08-09 17:19:21 +00:00
|
|
|
INP_UNLOCK(inp);
|
1995-03-20 18:11:31 +00:00
|
|
|
splx(s);
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case IP_MULTICAST_TTL:
|
|
|
|
/*
|
|
|
|
* Set the IP time-to-live for outgoing multicast packets.
|
1998-08-23 03:07:17 +00:00
|
|
|
* The original multicast API required a char argument,
|
|
|
|
* which is inconsistent with the rest of the socket API.
|
|
|
|
* We allow either a char or an int.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
1998-08-23 03:07:17 +00:00
|
|
|
if (sopt->sopt_valsize == 1) {
|
|
|
|
u_char ttl;
|
|
|
|
error = sooptcopyin(sopt, &ttl, 1, 1);
|
|
|
|
if (error)
|
|
|
|
break;
|
2005-08-09 17:19:21 +00:00
|
|
|
imo = ip_findmoptions(inp);
|
1998-08-23 03:07:17 +00:00
|
|
|
imo->imo_multicast_ttl = ttl;
|
2005-08-09 17:19:21 +00:00
|
|
|
INP_UNLOCK(inp);
|
1998-08-23 03:07:17 +00:00
|
|
|
} else {
|
|
|
|
u_int ttl;
|
|
|
|
error = sooptcopyin(sopt, &ttl, sizeof ttl,
|
|
|
|
sizeof ttl);
|
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
if (ttl > 255)
|
|
|
|
error = EINVAL;
|
2005-08-09 17:19:21 +00:00
|
|
|
else {
|
|
|
|
imo = ip_findmoptions(inp);
|
1998-08-23 03:07:17 +00:00
|
|
|
imo->imo_multicast_ttl = ttl;
|
2005-08-09 17:19:21 +00:00
|
|
|
INP_UNLOCK(inp);
|
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IP_MULTICAST_LOOP:
|
|
|
|
/*
|
|
|
|
* Set the loopback flag for outgoing multicast packets.
|
1998-08-23 03:07:17 +00:00
|
|
|
* Must be zero or one. The original multicast API required a
|
|
|
|
* char argument, which is inconsistent with the rest
|
|
|
|
* of the socket API. We allow either a char or an int.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
1998-08-23 03:07:17 +00:00
|
|
|
if (sopt->sopt_valsize == 1) {
|
|
|
|
u_char loop;
|
|
|
|
error = sooptcopyin(sopt, &loop, 1, 1);
|
|
|
|
if (error)
|
|
|
|
break;
|
2005-08-09 17:19:21 +00:00
|
|
|
imo = ip_findmoptions(inp);
|
1998-08-23 03:07:17 +00:00
|
|
|
imo->imo_multicast_loop = !!loop;
|
2005-08-09 17:19:21 +00:00
|
|
|
INP_UNLOCK(inp);
|
1998-08-23 03:07:17 +00:00
|
|
|
} else {
|
|
|
|
u_int loop;
|
|
|
|
error = sooptcopyin(sopt, &loop, sizeof loop,
|
|
|
|
sizeof loop);
|
|
|
|
if (error)
|
|
|
|
break;
|
2005-08-09 17:19:21 +00:00
|
|
|
imo = ip_findmoptions(inp);
|
1998-08-23 03:07:17 +00:00
|
|
|
imo->imo_multicast_loop = !!loop;
|
2005-08-09 17:19:21 +00:00
|
|
|
INP_UNLOCK(inp);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IP_ADD_MEMBERSHIP:
|
|
|
|
/*
|
|
|
|
* Add a multicast group membership.
|
|
|
|
* Group must be a valid IP multicast address.
|
|
|
|
*/
|
1998-08-23 03:07:17 +00:00
|
|
|
error = sooptcopyin(sopt, &mreq, sizeof mreq, sizeof mreq);
|
|
|
|
if (error)
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
1998-08-23 03:07:17 +00:00
|
|
|
|
|
|
|
if (!IN_MULTICAST(ntohl(mreq.imr_multiaddr.s_addr))) {
|
1994-05-24 10:09:53 +00:00
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
1995-03-20 18:31:51 +00:00
|
|
|
s = splimp();
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* If no interface address was provided, use the interface of
|
|
|
|
* the route to the given multicast address.
|
|
|
|
*/
|
1998-08-23 03:07:17 +00:00
|
|
|
if (mreq.imr_interface.s_addr == INADDR_ANY) {
|
1995-06-13 17:51:16 +00:00
|
|
|
bzero((caddr_t)&ro, sizeof(ro));
|
1994-05-24 10:09:53 +00:00
|
|
|
dst = (struct sockaddr_in *)&ro.ro_dst;
|
|
|
|
dst->sin_len = sizeof(*dst);
|
|
|
|
dst->sin_family = AF_INET;
|
1998-08-23 03:07:17 +00:00
|
|
|
dst->sin_addr = mreq.imr_multiaddr;
|
2003-11-20 20:07:39 +00:00
|
|
|
rtalloc_ign(&ro, RTF_CLONING);
|
1994-05-24 10:09:53 +00:00
|
|
|
if (ro.ro_rt == NULL) {
|
|
|
|
error = EADDRNOTAVAIL;
|
1995-03-20 18:11:31 +00:00
|
|
|
splx(s);
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
ifp = ro.ro_rt->rt_ifp;
|
2003-10-04 03:44:50 +00:00
|
|
|
RTFREE(ro.ro_rt);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
else {
|
2001-06-11 12:39:29 +00:00
|
|
|
ifp = ip_multicast_if(&mreq.imr_interface, NULL);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
1995-03-20 18:11:31 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* See if we found an interface, and confirm that it
|
|
|
|
* supports multicast.
|
|
|
|
*/
|
|
|
|
if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
|
|
|
|
error = EADDRNOTAVAIL;
|
1995-03-20 18:11:31 +00:00
|
|
|
splx(s);
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* See if the membership already exists or if all the
|
|
|
|
* membership slots are full.
|
|
|
|
*/
|
2005-08-09 17:19:21 +00:00
|
|
|
imo = ip_findmoptions(inp);
|
1994-05-24 10:09:53 +00:00
|
|
|
for (i = 0; i < imo->imo_num_memberships; ++i) {
|
|
|
|
if (imo->imo_membership[i]->inm_ifp == ifp &&
|
|
|
|
imo->imo_membership[i]->inm_addr.s_addr
|
1998-08-23 03:07:17 +00:00
|
|
|
== mreq.imr_multiaddr.s_addr)
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i < imo->imo_num_memberships) {
|
2005-08-09 17:19:21 +00:00
|
|
|
INP_UNLOCK(inp);
|
1994-05-24 10:09:53 +00:00
|
|
|
error = EADDRINUSE;
|
1995-03-20 18:11:31 +00:00
|
|
|
splx(s);
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i == IP_MAX_MEMBERSHIPS) {
|
2005-08-09 17:19:21 +00:00
|
|
|
INP_UNLOCK(inp);
|
1994-05-24 10:09:53 +00:00
|
|
|
error = ETOOMANYREFS;
|
1995-03-20 18:11:31 +00:00
|
|
|
splx(s);
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Everything looks good; add a new record to the multicast
|
|
|
|
* address list for the given interface.
|
|
|
|
*/
|
|
|
|
if ((imo->imo_membership[i] =
|
1998-08-23 03:07:17 +00:00
|
|
|
in_addmulti(&mreq.imr_multiaddr, ifp)) == NULL) {
|
2005-08-09 17:19:21 +00:00
|
|
|
INP_UNLOCK(inp);
|
1994-05-24 10:09:53 +00:00
|
|
|
error = ENOBUFS;
|
1995-03-20 18:11:31 +00:00
|
|
|
splx(s);
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
++imo->imo_num_memberships;
|
2005-08-09 17:19:21 +00:00
|
|
|
INP_UNLOCK(inp);
|
1995-03-20 18:11:31 +00:00
|
|
|
splx(s);
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case IP_DROP_MEMBERSHIP:
|
|
|
|
/*
|
|
|
|
* Drop a multicast group membership.
|
|
|
|
* Group must be a valid IP multicast address.
|
|
|
|
*/
|
1998-08-23 03:07:17 +00:00
|
|
|
error = sooptcopyin(sopt, &mreq, sizeof mreq, sizeof mreq);
|
|
|
|
if (error)
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
1998-08-23 03:07:17 +00:00
|
|
|
|
|
|
|
if (!IN_MULTICAST(ntohl(mreq.imr_multiaddr.s_addr))) {
|
1994-05-24 10:09:53 +00:00
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
1995-03-20 18:11:31 +00:00
|
|
|
|
1995-03-20 18:31:51 +00:00
|
|
|
s = splimp();
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* If an interface address was specified, get a pointer
|
|
|
|
* to its ifnet structure.
|
|
|
|
*/
|
1998-08-23 03:07:17 +00:00
|
|
|
if (mreq.imr_interface.s_addr == INADDR_ANY)
|
1994-05-24 10:09:53 +00:00
|
|
|
ifp = NULL;
|
|
|
|
else {
|
2001-06-11 12:39:29 +00:00
|
|
|
ifp = ip_multicast_if(&mreq.imr_interface, NULL);
|
1994-05-24 10:09:53 +00:00
|
|
|
if (ifp == NULL) {
|
|
|
|
error = EADDRNOTAVAIL;
|
1995-03-20 18:11:31 +00:00
|
|
|
splx(s);
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Find the membership in the membership array.
|
|
|
|
*/
|
2005-08-09 17:19:21 +00:00
|
|
|
imo = ip_findmoptions(inp);
|
1994-05-24 10:09:53 +00:00
|
|
|
for (i = 0; i < imo->imo_num_memberships; ++i) {
|
|
|
|
if ((ifp == NULL ||
|
|
|
|
imo->imo_membership[i]->inm_ifp == ifp) &&
|
|
|
|
imo->imo_membership[i]->inm_addr.s_addr ==
|
1998-08-23 03:07:17 +00:00
|
|
|
mreq.imr_multiaddr.s_addr)
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i == imo->imo_num_memberships) {
|
2005-08-09 17:19:21 +00:00
|
|
|
INP_UNLOCK(inp);
|
1994-05-24 10:09:53 +00:00
|
|
|
error = EADDRNOTAVAIL;
|
1995-03-20 18:11:31 +00:00
|
|
|
splx(s);
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Give up the multicast address record to which the
|
|
|
|
* membership points.
|
|
|
|
*/
|
|
|
|
in_delmulti(imo->imo_membership[i]);
|
|
|
|
/*
|
|
|
|
* Remove the gap in the membership array.
|
|
|
|
*/
|
|
|
|
for (++i; i < imo->imo_num_memberships; ++i)
|
|
|
|
imo->imo_membership[i-1] = imo->imo_membership[i];
|
|
|
|
--imo->imo_num_memberships;
|
2005-08-09 17:19:21 +00:00
|
|
|
INP_UNLOCK(inp);
|
1995-03-20 18:11:31 +00:00
|
|
|
splx(s);
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
error = EOPNOTSUPP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return the IP multicast options in response to user getsockopt().
|
|
|
|
*/
|
1995-11-14 20:34:56 +00:00
|
|
|
static int
|
2004-12-05 22:08:37 +00:00
|
|
|
ip_getmoptions(struct inpcb *inp, struct sockopt *sopt)
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2004-12-05 22:08:37 +00:00
|
|
|
struct ip_moptions *imo;
|
1998-08-23 03:07:17 +00:00
|
|
|
struct in_addr addr;
|
1994-05-24 10:09:53 +00:00
|
|
|
struct in_ifaddr *ia;
|
1998-08-23 03:07:17 +00:00
|
|
|
int error, optval;
|
|
|
|
u_char coptval;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2004-12-05 22:08:37 +00:00
|
|
|
INP_LOCK(inp);
|
|
|
|
imo = inp->inp_moptions;
|
|
|
|
|
1998-08-23 03:07:17 +00:00
|
|
|
error = 0;
|
|
|
|
switch (sopt->sopt_name) {
|
1995-06-13 17:51:16 +00:00
|
|
|
case IP_MULTICAST_VIF:
|
1994-09-06 22:42:31 +00:00
|
|
|
if (imo != NULL)
|
1998-08-23 03:07:17 +00:00
|
|
|
optval = imo->imo_multicast_vif;
|
1994-09-06 22:42:31 +00:00
|
|
|
else
|
1998-08-23 03:07:17 +00:00
|
|
|
optval = -1;
|
2004-12-05 22:08:37 +00:00
|
|
|
INP_UNLOCK(inp);
|
1998-08-23 03:07:17 +00:00
|
|
|
error = sooptcopyout(sopt, &optval, sizeof optval);
|
|
|
|
break;
|
1994-09-06 22:42:31 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
case IP_MULTICAST_IF:
|
|
|
|
if (imo == NULL || imo->imo_multicast_ifp == NULL)
|
1998-08-23 03:07:17 +00:00
|
|
|
addr.s_addr = INADDR_ANY;
|
2001-06-11 12:39:29 +00:00
|
|
|
else if (imo->imo_multicast_addr.s_addr) {
|
|
|
|
/* return the value user has set */
|
|
|
|
addr = imo->imo_multicast_addr;
|
|
|
|
} else {
|
1994-05-24 10:09:53 +00:00
|
|
|
IFP_TO_IA(imo->imo_multicast_ifp, ia);
|
1998-08-23 03:07:17 +00:00
|
|
|
addr.s_addr = (ia == NULL) ? INADDR_ANY
|
|
|
|
: IA_SIN(ia)->sin_addr.s_addr;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
2004-12-05 22:08:37 +00:00
|
|
|
INP_UNLOCK(inp);
|
1998-08-23 03:07:17 +00:00
|
|
|
error = sooptcopyout(sopt, &addr, sizeof addr);
|
|
|
|
break;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
case IP_MULTICAST_TTL:
|
1998-08-23 03:07:17 +00:00
|
|
|
if (imo == 0)
|
|
|
|
optval = coptval = IP_DEFAULT_MULTICAST_TTL;
|
|
|
|
else
|
|
|
|
optval = coptval = imo->imo_multicast_ttl;
|
2004-12-05 22:08:37 +00:00
|
|
|
INP_UNLOCK(inp);
|
1998-08-23 03:07:17 +00:00
|
|
|
if (sopt->sopt_valsize == 1)
|
|
|
|
error = sooptcopyout(sopt, &coptval, 1);
|
|
|
|
else
|
|
|
|
error = sooptcopyout(sopt, &optval, sizeof optval);
|
|
|
|
break;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
case IP_MULTICAST_LOOP:
|
1998-08-23 03:07:17 +00:00
|
|
|
if (imo == 0)
|
|
|
|
optval = coptval = IP_DEFAULT_MULTICAST_LOOP;
|
|
|
|
else
|
|
|
|
optval = coptval = imo->imo_multicast_loop;
|
2004-12-05 22:08:37 +00:00
|
|
|
INP_UNLOCK(inp);
|
1998-08-23 03:07:17 +00:00
|
|
|
if (sopt->sopt_valsize == 1)
|
|
|
|
error = sooptcopyout(sopt, &coptval, 1);
|
|
|
|
else
|
|
|
|
error = sooptcopyout(sopt, &optval, sizeof optval);
|
|
|
|
break;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
default:
|
2004-12-05 22:08:37 +00:00
|
|
|
INP_UNLOCK(inp);
|
1998-08-23 03:07:17 +00:00
|
|
|
error = ENOPROTOOPT;
|
|
|
|
break;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
2004-12-05 22:08:37 +00:00
|
|
|
INP_UNLOCK_ASSERT(inp);
|
|
|
|
|
1998-08-23 03:07:17 +00:00
|
|
|
return (error);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Discard the IP multicast options.
|
|
|
|
*/
|
|
|
|
void
|
2002-11-20 18:56:25 +00:00
|
|
|
ip_freemoptions(imo)
|
|
|
|
register struct ip_moptions *imo;
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2002-11-20 18:56:25 +00:00
|
|
|
register int i;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
if (imo != NULL) {
|
|
|
|
for (i = 0; i < imo->imo_num_memberships; ++i)
|
|
|
|
in_delmulti(imo->imo_membership[i]);
|
|
|
|
free(imo, M_IPMOPTS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Routine called from ip_output() to loop back a copy of an IP multicast
|
|
|
|
* packet to the input queue of a specified interface. Note that this
|
|
|
|
* calls the output routine of the loopback "driver", but with an interface
|
1995-04-26 18:10:58 +00:00
|
|
|
* pointer that might NOT be a loopback interface -- evil, but easier than
|
|
|
|
* replicating that code here.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
|
|
|
static void
|
2002-11-20 18:56:25 +00:00
|
|
|
ip_mloopback(ifp, m, dst, hlen)
|
|
|
|
struct ifnet *ifp;
|
|
|
|
register struct mbuf *m;
|
|
|
|
register struct sockaddr_in *dst;
|
|
|
|
int hlen;
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2002-11-20 18:56:25 +00:00
|
|
|
register struct ip *ip;
|
1994-05-24 10:09:53 +00:00
|
|
|
struct mbuf *copym;
|
|
|
|
|
2002-11-20 18:56:25 +00:00
|
|
|
copym = m_copy(m, 0, M_COPYALL);
|
1997-05-06 21:22:04 +00:00
|
|
|
if (copym != NULL && (copym->m_flags & M_EXT || copym->m_len < hlen))
|
|
|
|
copym = m_pullup(copym, hlen);
|
1994-05-24 10:09:53 +00:00
|
|
|
if (copym != NULL) {
|
2004-04-07 10:01:39 +00:00
|
|
|
/* If needed, compute the checksum and mark it as valid. */
|
|
|
|
if (copym->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
|
|
|
|
in_delayed_cksum(copym);
|
|
|
|
copym->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
|
|
|
|
copym->m_pkthdr.csum_flags |=
|
|
|
|
CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
|
|
|
|
copym->m_pkthdr.csum_data = 0xffff;
|
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* We don't bother to fragment if the IP length is greater
|
|
|
|
* than the interface's MTU. Can this possibly matter?
|
|
|
|
*/
|
|
|
|
ip = mtod(copym, struct ip *);
|
2002-02-18 20:35:27 +00:00
|
|
|
ip->ip_len = htons(ip->ip_len);
|
|
|
|
ip->ip_off = htons(ip->ip_off);
|
1994-05-24 10:09:53 +00:00
|
|
|
ip->ip_sum = 0;
|
2002-10-20 22:52:07 +00:00
|
|
|
ip->ip_sum = in_cksum(copym, hlen);
|
1996-04-18 15:49:06 +00:00
|
|
|
/*
|
|
|
|
* NB:
|
1997-02-28 19:40:48 +00:00
|
|
|
* It's not clear whether there are any lingering
|
|
|
|
* reentrancy problems in other areas which might
|
|
|
|
* be exposed by using ip_input directly (in
|
|
|
|
* particular, everything which modifies the packet
|
|
|
|
* in-place). Yet another option is using the
|
|
|
|
* protosw directly to deliver the looped back
|
|
|
|
* packet. For the moment, we'll err on the side
|
1998-06-12 03:48:19 +00:00
|
|
|
* of safety by using if_simloop().
|
1996-04-18 15:49:06 +00:00
|
|
|
*/
|
1998-06-14 20:58:17 +00:00
|
|
|
#if 1 /* XXX */
|
|
|
|
if (dst->sin_family != AF_INET) {
|
1998-06-15 00:35:47 +00:00
|
|
|
printf("ip_mloopback: bad address family %d\n",
|
1998-06-14 20:58:17 +00:00
|
|
|
dst->sin_family);
|
|
|
|
dst->sin_family = AF_INET;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
1996-04-18 15:49:06 +00:00
|
|
|
#ifdef notdef
|
1997-02-28 19:40:48 +00:00
|
|
|
copym->m_pkthdr.rcvif = ifp;
|
1998-06-12 03:48:19 +00:00
|
|
|
ip_input(copym);
|
1996-04-18 15:49:06 +00:00
|
|
|
#else
|
2000-05-24 21:16:56 +00:00
|
|
|
if_simloop(ifp, copym, dst->sin_family, 0);
|
1996-04-18 15:49:06 +00:00
|
|
|
#endif
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
}
|