1093 lines
27 KiB
C
1093 lines
27 KiB
C
/*
|
|
* Copyright (c) 1982, 1986, 1988, 1990 Regents of the University of California.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution is only permitted until one year after the first shipment
|
|
* of 4.4BSD by the Regents. Otherwise, redistribution and use in source and
|
|
* binary forms are permitted provided that: (1) source distributions retain
|
|
* this entire copyright notice and comment, and (2) distributions including
|
|
* binaries display the following acknowledgement: This product includes
|
|
* software developed by the University of California, Berkeley and its
|
|
* contributors'' in the documentation or other materials provided with the
|
|
* distribution and in all advertising materials mentioning features or use
|
|
* of this software. 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 AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
*
|
|
* @(#)udp_usrreq.c 7.7 (Berkeley) 6/29/88 plus MULTICAST 1.2
|
|
* plus parts of 7.18 (Berkeley) 7/25/90
|
|
*/
|
|
|
|
#include "tcp-param.h"
|
|
#include "sys/param.h"
|
|
#include "sys/debug.h"
|
|
#include "sys/sema.h"
|
|
#include "sys/hashing.h"
|
|
|
|
#include "sys/errno.h"
|
|
#include "sys/mbuf.h"
|
|
#include "sys/domain.h"
|
|
#include "sys/protosw.h"
|
|
#include "sys/socket.h"
|
|
#include "sys/socketvar.h"
|
|
#include "sys/tcpipstats.h"
|
|
#include "sys/sysmacros.h"
|
|
#include "sys/sesmgr.h"
|
|
#include "sys/mac_label.h"
|
|
|
|
#include "sys/systm.h"
|
|
#include "sys/rtmon.h"
|
|
#include "net/if.h"
|
|
#include "net/route.h"
|
|
|
|
#include "in.h"
|
|
#include "in_systm.h"
|
|
#include "in_var.h"
|
|
#ifdef INET6
|
|
#include "in6_var.h"
|
|
#endif
|
|
#include "ip.h"
|
|
#ifdef INET6
|
|
#include <netinet/ip6.h>
|
|
#endif
|
|
#include "in_pcb.h"
|
|
#include "ip_var.h"
|
|
#include "ip_icmp.h"
|
|
#include "icmp_var.h"
|
|
#include "udp.h"
|
|
#include "udp_var.h"
|
|
#ifdef INET6
|
|
#include "udp6_var.h"
|
|
#endif
|
|
|
|
#define UDP_MINHASHTABLESZ 64
|
|
#define UDP_MAXHASHTABLESZ 2048
|
|
|
|
extern int udp_hashtablesz;
|
|
extern int swipeflag; /* true if swipe is on */
|
|
|
|
/* Forward references. */
|
|
void udp_detach(struct inpcb *);
|
|
static struct mbuf *udp_saveopt(caddr_t, int, int);
|
|
|
|
/* External references. */
|
|
extern int in_pcb_hashtablesize(void);
|
|
|
|
/*
|
|
* UDP protocol implementation.
|
|
* Per RFC 768, August, 1980.
|
|
*/
|
|
void
|
|
udp_init(void)
|
|
{
|
|
if (udp_hashtablesz == 0) {
|
|
udp_hashtablesz = in_pcb_hashtablesize();
|
|
}
|
|
if (udp_hashtablesz < UDP_MINHASHTABLESZ)
|
|
udp_hashtablesz = UDP_MINHASHTABLESZ;
|
|
if (udp_hashtablesz > UDP_MAXHASHTABLESZ)
|
|
udp_hashtablesz = UDP_MAXHASHTABLESZ;
|
|
|
|
(void)in_pcbinitcb(&udb, udp_hashtablesz, INPFLAGS_CLTS, UDP_PCBSTAT);
|
|
|
|
return;
|
|
}
|
|
|
|
extern int udp_ttl;
|
|
struct inpcb udb; /* head of UDP protocol control block list */
|
|
int nfs_port = -1; /* "special" port number for NFS packets */
|
|
|
|
extern void nfs_input(struct mbuf *m); /* fast path hook to NFS */
|
|
|
|
#ifdef INET6
|
|
#define UDP_INP(inp) \
|
|
(((inp)->inp_flags & INP_COMPATV6) ? \
|
|
sin6tosa(&udp_in6) : ((struct sockaddr *)(&udp_in)))
|
|
#endif
|
|
|
|
#ifdef _UDP_UTRACE
|
|
#define UDPIN_UTRACE(name, mb, ra) \
|
|
UTRACE(RTMON_ALLOC, (name), (__int64_t)(mb), \
|
|
UTPACK((ra), (mb) ? (mb)->m_off : 0));
|
|
#else
|
|
#define UDPIN_UTRACE(name, mb, ra)
|
|
#endif
|
|
|
|
/*ARGSUSED*/
|
|
void
|
|
#ifdef INET6
|
|
udp_input(m, ifp, ipsec, unused)
|
|
#else
|
|
udp_input(m, ifp, ipsec)
|
|
#endif
|
|
struct mbuf *m;
|
|
struct ifnet *ifp;
|
|
struct ipsec *ipsec;
|
|
#ifdef INET6
|
|
struct mbuf *unused;
|
|
#endif
|
|
{
|
|
register struct udpiphdr *ui;
|
|
register struct inpcb *inp, *lastinp;
|
|
int len;
|
|
struct ip ip;
|
|
struct in_pcbhead *hinp;
|
|
u_short hashval;
|
|
struct mbuf *opts = 0;
|
|
|
|
struct sockaddr_in udp_in;
|
|
extern struct sockaddr_in in_zeroaddr;
|
|
#ifdef INET6
|
|
struct sockaddr_in6 udp_in6;
|
|
extern struct sockaddr_in6 in6_zeroaddr;
|
|
#endif
|
|
struct ifnet *ifns;
|
|
|
|
NETPAR(NETSCHED, NETEVENTKN, NETPID_NULL,
|
|
NETEVENT_UDPUP, NETCNT_NULL, NETRES_PROTCALL);
|
|
UDPSTAT(udps_ipackets);
|
|
|
|
/*
|
|
* ip_intr has pulled up entire IP header with options.
|
|
* Strip IP options, if any; should skip this,
|
|
* make available to user, and use on returned packets,
|
|
* but we don't yet have a way to check the checksum
|
|
* with options still present.
|
|
*
|
|
*/
|
|
ui = mtod(m, struct udpiphdr *);
|
|
if (((struct ip *)ui)->ip_hl > (sizeof (struct ip) >> 2)) {
|
|
#pragma mips_frequency_hint NEVER
|
|
ip_stripoptions(m, (struct mbuf *)0);
|
|
}
|
|
if (m->m_len < sizeof (struct udpiphdr)) {
|
|
#pragma mips_frequency_hint NEVER
|
|
if ((m = m_pullup(m, sizeof (struct udpiphdr))) == 0) {
|
|
UDPSTAT(udps_hdrops);
|
|
NETPAR(NETFLOW, NETDROPTKN, NETPID_NULL,
|
|
NETEVENT_UDPUP, m->m_len, NETRES_HEADER);
|
|
_SESMGR_SOATTR_FREE(ipsec);
|
|
return;
|
|
}
|
|
ui = mtod(m, struct udpiphdr *);
|
|
}
|
|
|
|
/*
|
|
* Make mbuf data length reflect UDP length.
|
|
* If not enough data to reflect UDP length, drop.
|
|
*/
|
|
len = ntohs((u_short)ui->ui_ulen);
|
|
if (((struct ip *)ui)->ip_len != len) {
|
|
if ((len > ((struct ip *)ui)->ip_len) ||
|
|
(len < sizeof (struct udphdr))) {
|
|
#pragma mips_frequency_hint NEVER
|
|
UDPSTAT(udps_badlen);
|
|
NETPAR(NETFLOW, NETDROPTKN, NETPID_NULL,
|
|
NETEVENT_UDPUP, len, NETRES_SIZE);
|
|
goto bad;
|
|
}
|
|
m_adj(m, len - ((struct ip *)ui)->ip_len);
|
|
}
|
|
/*
|
|
* If we get a zero length UDP packet, just silently drop it.
|
|
* It's not actually wrong, but passing it up the stack causes
|
|
* a multitude of problems.
|
|
*/
|
|
if (len == sizeof(struct udphdr)) {
|
|
#pragma mips_frequency_hint NEVER
|
|
goto bad;
|
|
}
|
|
/*
|
|
* Save a copy of the IP header in case we want to restore it for ICMP.
|
|
*/
|
|
ip = *(struct ip*)ui;
|
|
|
|
/*
|
|
* Checksum extended UDP header and data.
|
|
* Believe the link layer if possible.
|
|
*/
|
|
if (ui->ui_sum) {
|
|
ui->ui_next = ui->ui_prev = 0;
|
|
ui->ui_x1 = 0;
|
|
ui->ui_len = ui->ui_ulen;
|
|
if (!(m->m_flags & M_CKSUMMED) &&
|
|
in_cksum(m, len + sizeof (struct ip))) {
|
|
#pragma mips_frequency_hint NEVER
|
|
UDPSTAT(udps_badsum);
|
|
_SESMGR_SOATTR_FREE(ipsec);
|
|
m_freem(m);
|
|
NETPAR(NETFLOW, NETDROPTKN, NETPID_NULL,
|
|
NETEVENT_UDPUP, len, NETRES_CKSUM);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (ui->ui_dport == nfs_port) {
|
|
struct mbuf *tmp;
|
|
/*
|
|
* Special-case fast path for NFS
|
|
*/
|
|
tmp = _SESMGR_NFS_SET_IPSEC(ipsec, &m);
|
|
if (tmp != NULL) {
|
|
UDPIN_UTRACE(UTN('nfsi','nput'), tmp, __return_address);
|
|
nfs_input(tmp);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
udp_in = in_zeroaddr;
|
|
#ifdef INET6
|
|
udp_in6 = in6_zeroaddr;
|
|
#endif
|
|
|
|
if (!IN_MULTICAST(ntohl(ui->ui_dst.s_addr)) &&
|
|
!in_broadcast(ui->ui_dst, ifp)) {
|
|
/*
|
|
* Locate pcb for datagram.
|
|
* We don't use a cache here, since it's a wildcard lookup.
|
|
* Basically, we'll always fail the match, so skip it.
|
|
*/
|
|
inp = in_pcblookupx(&udb,
|
|
#ifdef INET6
|
|
&ui->ui_src, ui->ui_sport, &ui->ui_dst, ui->ui_dport,
|
|
INPLOOKUP_WILDCARD, AF_INET);
|
|
#else
|
|
ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport,
|
|
INPLOOKUP_WILDCARD);
|
|
#endif
|
|
if (inp == 0) {
|
|
struct in_addr null_inaddr = {0};
|
|
NETPAR(NETFLOW, NETDROPTKN, NETPID_NULL,
|
|
NETEVENT_UDPUP, len, NETRES_UNREACH);
|
|
UDPSTAT(udps_noport);
|
|
*(struct ip *)ui = ip;
|
|
ASSERT(ui == mtod(m, struct udpiphdr *));
|
|
|
|
icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT, ifp,
|
|
null_inaddr, NULL, ipsec);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Construct sockaddr format source address.
|
|
* Stuff source address and datagram in user buffer.
|
|
*/
|
|
#ifdef INET6
|
|
if (inp->inp_flags & INP_COMPATV6) {
|
|
udp_in6.sin6_port = ui->ui_sport;
|
|
udp_in6.sin6_flowinfo = 0;
|
|
udp_in6.sin6_addr.s6_addr32[0] = 0;
|
|
udp_in6.sin6_addr.s6_addr32[1] = 0;
|
|
udp_in6.sin6_addr.s6_addr32[2] = htonl(0xffff);
|
|
udp_in6.sin6_addr.s6_addr32[3] = ui->ui_src.s_addr;
|
|
} else {
|
|
udp_in.sin_port = ui->ui_sport;
|
|
udp_in.sin_addr = ui->ui_src;
|
|
}
|
|
#else
|
|
udp_in.sin_port = ui->ui_sport;
|
|
udp_in.sin_addr = ui->ui_src;
|
|
#endif
|
|
m->m_len -= sizeof (struct udpiphdr);
|
|
m->m_off += sizeof (struct udpiphdr);
|
|
if (inp->inp_socket->so_options & SO_PASSIFNAME)
|
|
ifns = ifp;
|
|
else
|
|
ifns = 0;
|
|
|
|
if (inp->inp_flags & INP_CONTROLOPTS) {
|
|
struct mbuf **mp = &opts;
|
|
|
|
if (inp->inp_flags & INP_RECVDSTADDR) {
|
|
*mp = udp_saveopt((caddr_t) &ui->ui_dst,
|
|
sizeof(struct in_addr), IP_RECVDSTADDR);
|
|
if (*mp)
|
|
mp = &(*mp)->m_next;
|
|
}
|
|
}
|
|
SOCKET_LOCK(inp->inp_socket);
|
|
#ifdef INET6
|
|
if (sbappendaddr(&inp->inp_socket->so_rcv,
|
|
UDP_INP(inp), m, opts, ifns) == 0) {
|
|
#else
|
|
if (sbappendaddr(&inp->inp_socket->so_rcv,
|
|
(struct sockaddr *)&udp_in, m, opts, ifns) == 0) {
|
|
#endif
|
|
struct socket *so2 = inp->inp_socket;
|
|
UDPSTAT(udps_fullsock);
|
|
NETPAR(NETFLOW, NETDROPTKN, NETPID_NULL,
|
|
NETEVENT_UDPUP, len, NETRES_SBFULL);
|
|
if (!INPCB_RELE(inp))
|
|
SOCKET_UNLOCK(so2);
|
|
goto bad;
|
|
}
|
|
NETPAR(NETFLOW, NETFLOWTKN, NETPID_NULL,
|
|
NETEVENT_UDPUP, len, NETRES_NULL);
|
|
|
|
/*
|
|
* Our cheezeball hack of using MT_RIGHTS for this requires us
|
|
* to free it here, since sbappendaddr() makes a copy.
|
|
*/
|
|
if (opts) {
|
|
m_freem(opts);
|
|
}
|
|
sorwakeup(inp->inp_socket, NETEVENT_UDPUP);
|
|
{ struct socket *so2 = inp->inp_socket;
|
|
if (!INPCB_RELE(inp))
|
|
SOCKET_UNLOCK(so2);
|
|
}
|
|
_SESMGR_SOATTR_FREE(ipsec);
|
|
return;
|
|
} else {
|
|
struct socket *last;
|
|
struct inpcb *inpnxt;
|
|
struct inpcb *head = &udb;
|
|
int done = 0;
|
|
|
|
/*
|
|
* Deliver a multicast or broadcast datagram to *all* sockets
|
|
* for which the local and remote addresses and ports match
|
|
* those of the incoming datagram. This allows more than
|
|
* one process to receive multi/broadcasts on the same port.
|
|
* (This really ought to be done for unicast datagrams as
|
|
* well, but that would cause problems with existing
|
|
* applications that open both address-specific sockets and
|
|
* a wildcard socket listening to the same port -- they would
|
|
* end up receiving duplicates of every unicast datagram.
|
|
* Those applications open the multiple sockets to overcome an
|
|
* inadequacy of the UDP socket interface, but for backwards
|
|
* compatibility we avoid the problem here rather than
|
|
* fixing the interface. Maybe 4.4BSD will remedy this?)
|
|
*/
|
|
|
|
/*
|
|
* Construct sockaddr format source address.
|
|
*/
|
|
udp_in.sin_port = ui->ui_sport;
|
|
udp_in.sin_addr = ui->ui_src;
|
|
#ifdef INET6
|
|
udp_in6.sin6_port = ui->ui_sport;
|
|
udp_in6.sin6_flowinfo = 0;
|
|
udp_in6.sin6_addr.s6_addr32[0] = 0;
|
|
udp_in6.sin6_addr.s6_addr32[1] = 0;
|
|
udp_in6.sin6_addr.s6_addr32[2] = htonl(0xffff);
|
|
udp_in6.sin6_addr.s6_addr32[3] = ui->ui_src.s_addr;
|
|
#endif
|
|
m->m_len -= sizeof (struct udpiphdr);
|
|
m->m_off += sizeof (struct udpiphdr);
|
|
/*
|
|
* Locate pcb(s) for datagram.
|
|
* (Algorithm copied from raw_intr().)
|
|
*/
|
|
#ifdef INET6
|
|
hashval = (*head->inp_hashfun)(head, &ui->ui_src,
|
|
ui->ui_sport, &ui->ui_dst, ui->ui_dport, AF_INET);
|
|
#else
|
|
hashval = (*head->inp_hashfun)(head, ui->ui_src,
|
|
ui->ui_sport, ui->ui_dst, ui->ui_dport);
|
|
#endif
|
|
|
|
/*
|
|
* Pass destination address so we can later decide if the
|
|
* packet is multicast or not
|
|
*/
|
|
if (sesmgr_enabled) {
|
|
struct mbuf **mp = &opts;
|
|
|
|
*mp = udp_saveopt((caddr_t) &ui->ui_dst,
|
|
sizeof(struct in_addr),
|
|
IP_RECVDSTADDR);
|
|
if (*mp)
|
|
mp = &(*mp)->m_next;
|
|
}
|
|
resync:
|
|
/*
|
|
* This section of code takes advantage of the fact that
|
|
* we know that UDP inpcb's hash into exactly one
|
|
* list per port number.
|
|
*/
|
|
last = NULL;
|
|
hinp = &head->inp_table[hashval];
|
|
INHHEAD_LOCK(hinp);
|
|
for (inp = hinp->hinp_next; !done &&
|
|
inp != (struct inpcb *)hinp; inp = inpnxt) {
|
|
ASSERT(INHHEAD_ISLOCKED(hinp));
|
|
inpnxt = inp->inp_next;
|
|
/*
|
|
* Make sure sockets with no multicast options
|
|
* do not get multicast packets.
|
|
*/
|
|
if (IN_MULTICAST(ntohl(ui->ui_dst.s_addr)) &&
|
|
(inp->inp_moptions == NULL)) {
|
|
continue;
|
|
}
|
|
if (inp->inp_lport != ui->ui_dport) {
|
|
continue;
|
|
}
|
|
#ifdef INET6
|
|
if ((inp->inp_flags & INP_COMPATV4) == 0)
|
|
continue;
|
|
if (inp->inp_latype != IPATYPE_UNBD) {
|
|
if ((inp->inp_latype & IPATYPE_IPV4) == 0 ||
|
|
inp->inp_laddr.s_addr != ui->ui_dst.s_addr)
|
|
continue;
|
|
}
|
|
if (inp->inp_fatype != IPATYPE_UNBD) {
|
|
if ((inp->inp_fatype & IPATYPE_IPV4) == 0 ||
|
|
inp->inp_faddr.s_addr != ui->ui_src.s_addr ||
|
|
inp->inp_fport != ui->ui_sport)
|
|
continue;
|
|
}
|
|
#else
|
|
if (inp->inp_laddr.s_addr != INADDR_ANY) {
|
|
if (inp->inp_laddr.s_addr !=
|
|
ui->ui_dst.s_addr)
|
|
continue;
|
|
}
|
|
if (inp->inp_faddr.s_addr != INADDR_ANY) {
|
|
if (inp->inp_faddr.s_addr !=
|
|
ui->ui_src.s_addr ||
|
|
inp->inp_fport != ui->ui_sport)
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
INPCB_HOLD(inp);
|
|
if (last != NULL) {
|
|
struct mbuf *n;
|
|
|
|
if ((n = m_copy(m, 0, M_COPYALL)) != NULL) {
|
|
lastinp = sotoinpcb(last);
|
|
INHHEAD_UNLOCK(hinp);
|
|
SOCKET_LOCK(last);
|
|
if (last->so_options & SO_PASSIFNAME)
|
|
ifns = ifp;
|
|
else
|
|
ifns = 0;
|
|
if (sbappendaddr(&last->so_rcv,
|
|
#ifdef INET6
|
|
UDP_INP(sotoinpcb(last)), n,
|
|
#else
|
|
(struct sockaddr *)&udp_in, n,
|
|
#endif
|
|
opts, ifns) == 0) {
|
|
m_freem(n);
|
|
UDPSTAT(udps_fullsock);
|
|
NETPAR(NETFLOW, NETDROPTKN,
|
|
NETPID_NULL, NETEVENT_UDPUP,
|
|
len, NETRES_SBFULL);
|
|
} else {
|
|
sorwakeup(last, NETEVENT_UDPUP);
|
|
}
|
|
if (!INPCB_RELE(lastinp))
|
|
SOCKET_UNLOCK(last);
|
|
INHHEAD_LOCK(hinp);
|
|
if (inp->inp_next != inpnxt) {
|
|
struct socket *so =
|
|
inp->inp_socket;
|
|
|
|
INHHEAD_UNLOCK(hinp);
|
|
SOCKET_LOCK(so);
|
|
if (!INPCB_RELE(inp))
|
|
SOCKET_UNLOCK(so);
|
|
goto resync;
|
|
}
|
|
}
|
|
}
|
|
last = inp->inp_socket;
|
|
/*
|
|
* Don't look for additional matches if this one does
|
|
* not have one of the SO_REUSE{ADDR,PORT} options set.
|
|
* This heuristic avoids searching through all pcbs
|
|
* in the common case of a non-shared port. It
|
|
* assumes that an application will never clear
|
|
* the SO_REUSE{ADDR,PORT} option after setting it.
|
|
*/
|
|
if ((last->so_options &
|
|
(SO_REUSEADDR|SO_REUSEPORT))==0) {
|
|
done = 1;
|
|
break;
|
|
}
|
|
} /* per-hash for loop */
|
|
INHHEAD_UNLOCK(hinp);
|
|
|
|
if (last == NULL) {
|
|
/*
|
|
* No matching pcb found; discard datagram.
|
|
* (No need to send an ICMP Port Unreachable
|
|
* for a broadcast or multicast datgram.)
|
|
*/
|
|
|
|
NETPAR(NETFLOW, NETDROPTKN, NETPID_NULL,
|
|
NETEVENT_UDPUP, len, NETRES_UNREACH);
|
|
UDPSTAT(udps_noportbcast);
|
|
goto bad;
|
|
}
|
|
lastinp = sotoinpcb(last);
|
|
SOCKET_LOCK(last);
|
|
if (last->so_options & SO_PASSIFNAME)
|
|
ifns = ifp;
|
|
else
|
|
ifns = 0;
|
|
#ifdef INET6
|
|
if (sbappendaddr(&last->so_rcv, UDP_INP(sotoinpcb(last)),
|
|
#else
|
|
if (sbappendaddr(&last->so_rcv, (struct sockaddr *)&udp_in,
|
|
#endif
|
|
m, opts, ifns) == 0)
|
|
{
|
|
UDPSTAT(udps_fullsock);
|
|
NETPAR(NETFLOW, NETDROPTKN, NETPID_NULL,
|
|
NETEVENT_UDPUP, len, NETRES_SBFULL);
|
|
if (!INPCB_RELE(lastinp))
|
|
SOCKET_UNLOCK(last);
|
|
goto bad;
|
|
}
|
|
|
|
NETPAR(NETFLOW, NETFLOWTKN, NETPID_NULL,
|
|
NETEVENT_UDPUP, len, NETRES_NULL);
|
|
sorwakeup(last, NETEVENT_UDPUP);
|
|
if (!INPCB_RELE(lastinp))
|
|
SOCKET_UNLOCK(last);
|
|
_SESMGR_SOATTR_FREE(ipsec);
|
|
if (opts) {
|
|
m_freem(opts);
|
|
}
|
|
return;
|
|
}
|
|
bad:
|
|
_SESMGR_SOATTR_FREE(ipsec);
|
|
m_freem(m);
|
|
if (opts) {
|
|
m_freem(opts);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Notify a udp user of an asynchronous error;
|
|
* just wake up so that he can collect error status.
|
|
*/
|
|
#ifdef sgi
|
|
/*ARGSUSED*/
|
|
void
|
|
udp_notify(
|
|
register struct inpcb *inp,
|
|
int errno,
|
|
void *data)
|
|
#else
|
|
udp_notify(inp, errno)
|
|
register struct inpcb *inp;
|
|
int errno;
|
|
#endif
|
|
{
|
|
inp->inp_socket->so_error = errno;
|
|
sorwakeup(inp->inp_socket, NETEVENT_UDPUP);
|
|
sowwakeup(inp->inp_socket, NETEVENT_UDPUP);
|
|
}
|
|
|
|
#ifdef sgi /* MTUDisc */
|
|
void
|
|
udp_ctlinput(cmd, sa, icp)
|
|
int cmd;
|
|
struct sockaddr *sa;
|
|
struct icmp *icp;
|
|
#else
|
|
udp_ctlinput(cmd, sa, ip)
|
|
int cmd;
|
|
struct sockaddr *sa;
|
|
register struct ip *ip;
|
|
#endif /* MTUDisc */
|
|
{
|
|
register struct udphdr *uh;
|
|
struct in_addr zeroin_addr;
|
|
extern u_char inetctlerrmap[];
|
|
struct in_addr src;
|
|
register u_short dport = 0, sport = 0;
|
|
#ifdef sgi /* MTUDisc */
|
|
register struct ip *ip = icp ? &icp->icmp_ip : NULL;
|
|
#endif /* MTUDisc */
|
|
|
|
zeroin_addr.s_addr = 0;
|
|
|
|
if (!PRC_IS_REDIRECT(cmd) &&
|
|
((unsigned)cmd >= PRC_NCMDS || inetctlerrmap[cmd] == 0))
|
|
return;
|
|
if (ip) {
|
|
uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2));
|
|
if (((caddr_t)uh+sizeof(uh->uh_sport)+sizeof(uh->uh_dport)) <=
|
|
(caddr_t)ip + ip->ip_len) {
|
|
sport = uh->uh_sport;
|
|
dport = uh->uh_dport;
|
|
}
|
|
src = ip->ip_src;
|
|
} else
|
|
src = zeroin_addr;
|
|
#ifdef sgi
|
|
in_pcbnotify(&udb, sa, dport, src, sport, cmd, udp_notify, 0);
|
|
#else
|
|
in_pcbnotify(&udb, sa, dport, src, sport, cmd, udp_notify);
|
|
#endif
|
|
}
|
|
|
|
udp_output(
|
|
register struct inpcb *inp,
|
|
struct mbuf *m0,
|
|
struct inaddrpair *iap,
|
|
struct ipsec * ipsec)
|
|
{
|
|
register struct mbuf *m;
|
|
register struct udpiphdr *ui;
|
|
register int len = 0;
|
|
__uint32_t cksum;
|
|
struct ifnet *ifp;
|
|
int do_fastpath = 0;
|
|
int error = 0;
|
|
|
|
ASSERT(SOCKET_ISLOCKED(inp->inp_socket));
|
|
/*
|
|
* Calculate data length and get a mbuf
|
|
* for UDP and IP headers if needed.
|
|
*/
|
|
NETPAR(NETSCHED, NETEVENTKN, NETPID_NULL,
|
|
NETEVENT_UDPDOWN, NETCNT_NULL, NETRES_PROTCALL);
|
|
for (m = m0; m; m = m->m_next)
|
|
len += m->m_len;
|
|
if (len > (IP_MAXPACKET - sizeof(struct udpiphdr))) {
|
|
NETPAR(NETFLOW, NETDROPTKN, NETPID_NULL,
|
|
NETEVENT_UDPDOWN, len, NETRES_SIZE);
|
|
m_freem(m0);
|
|
return (EMSGSIZE);
|
|
}
|
|
if (m0 && m_hasroom(m0, sizeof (struct udpiphdr))) {
|
|
/*
|
|
* Just use the room in the mbuf we already have
|
|
*/
|
|
m = m0;
|
|
m->m_off -= sizeof (struct udpiphdr);
|
|
m->m_len += sizeof (struct udpiphdr);
|
|
} else {
|
|
MGET(m, M_DONTWAIT, MT_HEADER);
|
|
if (m == 0) {
|
|
NETPAR(NETFLOW, NETDROPTKN, NETPID_NULL,
|
|
NETEVENT_UDPDOWN, len, NETRES_MBUF);
|
|
m_freem(m0);
|
|
return (ENOBUFS);
|
|
}
|
|
m->m_off = MMAXOFF - sizeof (struct udpiphdr);
|
|
m->m_len = sizeof (struct udpiphdr);
|
|
m->m_next = m0;
|
|
}
|
|
|
|
/*
|
|
* Fill in mbuf with extended UDP header
|
|
* and addresses and length put into network format.
|
|
*/
|
|
ui = mtod(m, struct udpiphdr *);
|
|
ui->ui_next = ui->ui_prev = 0;
|
|
ui->ui_x1 = 0;
|
|
ui->ui_pr = IPPROTO_UDP;
|
|
ui->ui_len = htons((u_short)len + sizeof (struct udphdr));
|
|
ui->ui_src = iap->iap_laddr;
|
|
ui->ui_dst = iap->iap_faddr;
|
|
ui->ui_sport = iap->iap_lport;
|
|
ui->ui_dport = iap->iap_fport;
|
|
ui->ui_ulen = ui->ui_len;
|
|
|
|
/*
|
|
* Stuff checksum (required now) and output datagram.
|
|
*/
|
|
ui->ui_sum = 0;
|
|
{
|
|
/*
|
|
* Let the link layer compute the checksum if it wants
|
|
*/
|
|
struct rtentry *rt;
|
|
|
|
if ((rt = inp->inp_route.ro_rt)
|
|
&& 0 != (ifp = rt->rt_ifp)
|
|
&& sizeof(*ui) + len <= ifp->if_mtu
|
|
&& 0 != (rt->rt_flags & RTF_UP)
|
|
&& (((struct sockaddr_in *)&inp->inp_route.ro_dst
|
|
)->sin_addr.s_addr == ui->ui_dst.s_addr)
|
|
&& !IN_MULTICAST(ntohl(ui->ui_dst.s_addr))) {
|
|
if ((0 != (ifp->if_flags & IFF_CKSUM)) ||
|
|
(0 != (rt->rt_flags & RTF_CKSUM))) {
|
|
ui->ui_sum = 0xffff;
|
|
m->m_flags |= M_CKSUMMED;
|
|
} else {
|
|
if ((ui->ui_sum = in_cksum(m, sizeof(*ui)
|
|
+ len)) == 0) {
|
|
ui->ui_sum = 0xffff;
|
|
}
|
|
}
|
|
if (inp->inp_options == 0 &&
|
|
inp->inp_moptions == 0 &&
|
|
!(inp->inp_socket->so_options &
|
|
(SO_DONTROUTE|SO_BROADCAST))) {
|
|
do_fastpath = 1;
|
|
}
|
|
} else {
|
|
if ((ui->ui_sum = in_cksum(m, sizeof(*ui)+len)) == 0) {
|
|
ui->ui_sum = 0xffff;
|
|
}
|
|
}
|
|
}
|
|
((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
|
|
((struct ip *)ui)->ip_ttl = inp->inp_ip_ttl; /* XXX */
|
|
((struct ip *)ui)->ip_tos = inp->inp_ip_tos; /* XXX */
|
|
NETPAR(NETFLOW, NETFLOWTKN, NETPID_NULL,
|
|
NETEVENT_UDPDOWN, len, NETRES_NULL);
|
|
UDPSTAT(udps_opackets);
|
|
|
|
if (do_fastpath && !swipeflag && !sesmgr_enabled) {
|
|
struct ip *ip = (struct ip *)ui;
|
|
struct sockaddr *dst;
|
|
(*(char *)ip) = 0x45; /* IP vers and header len */
|
|
ip->ip_id = htons(atomicAddIntHot(&ip_id, 1));
|
|
ip->ip_off = 0;
|
|
#define ckp ((ushort*)ip)
|
|
cksum = (ckp[0] + ckp[1] + ckp[2] + ckp[3] + ckp[4]
|
|
+ ckp[6] + ckp[7] + ckp[8] + ckp[9]);
|
|
cksum = (cksum & 0xffff) + (cksum >> 16);
|
|
cksum = (cksum & 0xffff) + (cksum >> 16);
|
|
ip->ip_sum = cksum ^ 0xffff;
|
|
IPSTAT(ips_localout);
|
|
IFNET_UPPERLOCK(ifp);
|
|
dst = (inp->inp_route.ro_rt->rt_flags & RTF_GATEWAY) ?
|
|
inp->inp_route.ro_rt->rt_gateway :
|
|
&inp->inp_route.ro_dst;
|
|
error = (*ifp->if_output)(ifp, m, dst, inp->inp_route.ro_rt);
|
|
IFNET_UPPERUNLOCK(ifp);
|
|
return error;
|
|
}
|
|
return ip_output(m, inp->inp_options, &inp->inp_route,
|
|
(inp->inp_socket->so_options & (SO_DONTROUTE |
|
|
SO_BROADCAST))
|
|
| IP_MULTICASTOPTS, inp->inp_moptions, ipsec);
|
|
|
|
}
|
|
|
|
extern u_int udp_sendspace, udp_recvgrams;
|
|
|
|
/*ARGSUSED*/
|
|
udp_usrreq(so, req, m, nam, rights)
|
|
struct socket *so;
|
|
int req;
|
|
struct mbuf *m, *nam, *rights;
|
|
{
|
|
struct inpcb *inp = sotoinpcb(so);
|
|
int error = 0;
|
|
u_long udp_recvspace;
|
|
struct in_addr oladdr;
|
|
struct in_addr *from = 0;
|
|
struct ipsec *ipsec = so->so_sesmgr_data;
|
|
|
|
ASSERT(SOCKET_ISLOCKED(so));
|
|
if (req == PRU_CONTROL) {
|
|
#ifdef INET6
|
|
if (sotopf(so) == AF_INET)
|
|
return (in_control(so, (__psint_t)m, (caddr_t)nam,
|
|
(struct ifnet *)rights));
|
|
else
|
|
return (in6_control(so, (__psint_t)m, (caddr_t)nam,
|
|
(struct ifnet *)rights));
|
|
#else
|
|
return (in_control(so, (__psint_t)m, (caddr_t)nam,
|
|
(struct ifnet *)rights));
|
|
#endif
|
|
}
|
|
if ((req != PRU_SEND) && rights && rights->m_len) {
|
|
error = EINVAL;
|
|
goto release;
|
|
}
|
|
if (inp == NULL && req != PRU_ATTACH) {
|
|
error = EINVAL;
|
|
goto release;
|
|
}
|
|
/*
|
|
* Note: need to block udp_input while changing
|
|
* the udp pcb queue and/or pcb addresses.
|
|
*/
|
|
switch (req) {
|
|
|
|
case PRU_ATTACH:
|
|
if (inp != NULL) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
error = in_pcballoc(so, &udb);
|
|
if (error)
|
|
break;
|
|
udp_recvspace = udp_recvgrams
|
|
#ifdef INET6
|
|
* (udp_sendspace+sizeof(struct sockaddr_in6));
|
|
#else
|
|
* (udp_sendspace+sizeof(struct sockaddr_in));
|
|
#endif
|
|
soreserve(so, udp_sendspace, udp_recvspace);
|
|
#ifdef INET6
|
|
if (sotopf(so) == AF_INET)
|
|
((struct inpcb *) so->so_pcb)->inp_flags = INP_COMPATV4;
|
|
else
|
|
((struct inpcb *)so->so_pcb)->inp_flags = INP_COMPATANY;
|
|
#endif
|
|
((struct inpcb *) so->so_pcb)->inp_ip_ttl = udp_ttl;
|
|
break;
|
|
|
|
case PRU_DETACH:
|
|
udp_detach(inp);
|
|
inp = 0;
|
|
break;
|
|
|
|
case PRU_BIND:
|
|
#ifdef INET6
|
|
if (sotopf(so) == AF_INET)
|
|
error = in_pcbbind(inp, nam);
|
|
else
|
|
error = in6_pcbbind(inp, nam);
|
|
#else
|
|
error = in_pcbbind(inp, nam);
|
|
#endif
|
|
break;
|
|
|
|
case PRU_LISTEN:
|
|
error = EOPNOTSUPP;
|
|
break;
|
|
|
|
case PRU_CONNECT:
|
|
#ifdef INET6
|
|
if (inp->inp_fatype != IPATYPE_UNBD) {
|
|
#else
|
|
if (inp->inp_faddr.s_addr != INADDR_ANY) {
|
|
#endif
|
|
error = EISCONN;
|
|
break;
|
|
}
|
|
NETPAR(NETSCHED, NETEVENTKN, NETPID_NULL,
|
|
NETEVENT_UDPDOWN, NETCNT_NULL, NETRES_CONN);
|
|
#ifdef INET6
|
|
if (sotopf(so) == AF_INET)
|
|
error = in_pcbconnect(inp, nam);
|
|
else
|
|
error = in6_pcbconnect(inp, nam);
|
|
#else
|
|
error = in_pcbconnect(inp, nam);
|
|
#endif
|
|
NETPAR(NETSCHED, NETEVENTKN, NETPID_NULL,
|
|
NETEVENT_UDPDOWN, NETCNT_NULL, NETRES_CONNDONE);
|
|
if (error == 0)
|
|
soisconnected(so);
|
|
break;
|
|
|
|
case PRU_CONNECT2:
|
|
error = EOPNOTSUPP;
|
|
break;
|
|
|
|
case PRU_ACCEPT:
|
|
error = EOPNOTSUPP;
|
|
break;
|
|
|
|
case PRU_DISCONNECT:
|
|
#ifdef INET6
|
|
if (inp->inp_fatype == IPATYPE_UNBD) {
|
|
#else
|
|
if (inp->inp_faddr.s_addr == INADDR_ANY) {
|
|
#endif
|
|
error = ENOTCONN;
|
|
break;
|
|
}
|
|
in_pcbdisconnect(inp);
|
|
#ifdef INET6
|
|
CLR_ADDR6(inp->inp_laddr6);
|
|
inp->inp_latype = IPATYPE_UNBD;
|
|
inp->inp_flags |= INP_COMPATANY;
|
|
inp->inp_iflowinfo = 0;
|
|
#else
|
|
inp->inp_laddr.s_addr = INADDR_ANY;
|
|
#endif
|
|
so->so_state &= ~SS_ISCONNECTED; /* XXX */
|
|
break;
|
|
|
|
case PRU_SHUTDOWN:
|
|
socantsendmore(so);
|
|
break;
|
|
|
|
case PRU_SEND: {
|
|
struct inaddrpair ipr;
|
|
struct sockaddr_in *sin;
|
|
|
|
#ifdef INET6
|
|
/*
|
|
* The sendmsg() hack below is not needed for v6 because
|
|
* the equivalent functionality exists as part of the
|
|
* IPV6_PKTINFO option.
|
|
*/
|
|
if (sotopf(so) == AF_INET6)
|
|
return (udp6_output(inp, m, nam, rights));
|
|
#endif
|
|
if (nam) {
|
|
/*
|
|
* Hack to allow sendmsg() call to specify source
|
|
* address to use. This avoids having to open
|
|
* thousands of sockets when using large numbers of
|
|
* aliases.
|
|
*/
|
|
oladdr = inp->inp_laddr;
|
|
if (rights) {
|
|
struct cmsghdr *cmsg;
|
|
struct sockaddr_in sin;
|
|
#if _MIPS_SIM == _ABI64
|
|
extern int irix5_to_cmsghdr(struct mbuf *);
|
|
|
|
irix5_to_cmsghdr(rights);
|
|
#endif /* _ABI64 */
|
|
cmsg = mtod(rights, struct cmsghdr *);
|
|
bzero((caddr_t)&sin, sizeof(sin));
|
|
#define _LEN (sizeof(*cmsg) + sizeof(*from))
|
|
if (rights->m_len < _LEN) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
if (cmsg->cmsg_len != _LEN ||
|
|
cmsg->cmsg_level != IPPROTO_IP ||
|
|
cmsg->cmsg_type != IP_SENDSRCADDR) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
from = (struct in_addr *)CMSG_DATA(cmsg);
|
|
sin.sin_family = AF_INET;
|
|
sin.sin_addr = *from;
|
|
|
|
/* must be one of our addresses */
|
|
if (!ifa_ifwithaddr((struct sockaddr *)&sin)) {
|
|
error = EADDRNOTAVAIL;
|
|
break;
|
|
}
|
|
}
|
|
#undef _LEN
|
|
#ifdef INET6
|
|
if (inp->inp_fatype != IPATYPE_UNBD) {
|
|
#else
|
|
if (inp->inp_faddr.s_addr != INADDR_ANY) {
|
|
#endif
|
|
error = EISCONN;
|
|
break;
|
|
}
|
|
sin = mtod(nam, struct sockaddr_in *);
|
|
if (nam->m_len != sizeof(*sin)) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
if (from) {
|
|
if (inp->inp_lport == 0) {
|
|
error = in_pcbbind(inp,
|
|
(struct mbuf *)0);
|
|
if (error) {
|
|
break;
|
|
}
|
|
}
|
|
inp->inp_laddr = *from;
|
|
}
|
|
if (error = in_pcbsetaddrx(inp, sin, inp->inp_laddr,
|
|
&ipr)) {
|
|
inp->inp_laddr = oladdr;
|
|
break;
|
|
}
|
|
error = udp_output(inp, m, &ipr, ipsec);
|
|
inp->inp_laddr = oladdr;
|
|
} else {
|
|
#ifdef INET6
|
|
if (inp->inp_fatype == IPATYPE_UNBD) {
|
|
#else
|
|
if (inp->inp_faddr.s_addr == INADDR_ANY) {
|
|
#endif
|
|
error = ENOTCONN;
|
|
break;
|
|
}
|
|
error = udp_output(inp, m, &inp->inp_iap, ipsec);
|
|
}
|
|
m = NULL;
|
|
}
|
|
break;
|
|
|
|
case PRU_ABORT:
|
|
soisdisconnected(so);
|
|
udp_detach(inp);
|
|
break;
|
|
|
|
case PRU_SOCKADDR:
|
|
in_setsockaddr(inp, nam);
|
|
break;
|
|
|
|
case PRU_PEERADDR:
|
|
in_setpeeraddr(inp, nam);
|
|
break;
|
|
|
|
case PRU_SENSE:
|
|
/*
|
|
* stat: don't bother with a blocksize.
|
|
*/
|
|
return (0);
|
|
|
|
case PRU_SENDOOB:
|
|
case PRU_FASTTIMO:
|
|
case PRU_SLOWTIMO:
|
|
case PRU_PROTORCV:
|
|
case PRU_PROTOSEND:
|
|
error = EOPNOTSUPP;
|
|
break;
|
|
|
|
case PRU_RCVD:
|
|
case PRU_RCVOOB:
|
|
return (EOPNOTSUPP); /* do not free mbuf's */
|
|
|
|
case PRU_SOCKLABEL:
|
|
if (!sesmgr_enabled)
|
|
error = EOPNOTSUPP;
|
|
else
|
|
sesmgr_set_label(inp->inp_socket,(mac_label *)nam);
|
|
break;
|
|
|
|
default:
|
|
panic("udp_usrreq");
|
|
}
|
|
release:
|
|
if (m != NULL)
|
|
m_freem(m);
|
|
return (error);
|
|
}
|
|
|
|
#ifdef sgi
|
|
void
|
|
#endif
|
|
udp_detach(struct inpcb *inp)
|
|
{
|
|
in_pcbdetach(inp);
|
|
}
|
|
|
|
/*
|
|
* Create a "control" mbuf containing the specified data
|
|
* with the specified type for presentation with a datagram.
|
|
*/
|
|
struct mbuf *
|
|
udp_saveopt(caddr_t p, int size, int type)
|
|
{
|
|
register struct cmsghdr *cp;
|
|
struct mbuf *m;
|
|
|
|
if ((m = m_get(M_DONTWAIT, MT_RIGHTS)) == NULL) /* XXX control */
|
|
return ((struct mbuf *) NULL);
|
|
cp = (struct cmsghdr *) mtod(m, struct cmsghdr *);
|
|
bcopy(p, CMSG_DATA(cp), size);
|
|
size += sizeof(*cp);
|
|
m->m_len = size;
|
|
cp->cmsg_len = size;
|
|
cp->cmsg_level = IPPROTO_IP;
|
|
cp->cmsg_type = type;
|
|
return (m);
|
|
}
|