* Copyright (c) 1982, 1986, 1988, 1990, 1993
* 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
* 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* 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
* from: @(#)udp_usrreq.c 7.20 (Berkeley) 4/20/91
* $Id: udp_usrreq.c,v 1.7 1994/02/07 19:53:25 ache Exp $
#include "../net/route.h"
struct inpcb udb
; /* Can't be static, because of netstat want it */
static struct inpcb
*udp_last_inpcb
= &udb
;
static void udp_detach(struct inpcb
*);
* UDP protocol implementation.
* Per RFC 768, August, 1980.
udb
.inp_next
= udb
.inp_prev
= &udb
;
static struct sockaddr_in udp_in
= { sizeof(udp_in
), AF_INET
};
register struct udphdr
*uh
;
register struct inpcb
*inp
;
* 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.
if (iphlen
> sizeof (struct ip
)) {
ip_stripoptions(m
, (struct mbuf
*)0);
iphlen
= sizeof(struct ip
);
* Get IP and UDP header together in first mbuf.
ip
= mtod(m
, struct ip
*);
if (m
->m_len
< iphlen
+ sizeof(struct udphdr
)) {
if ((m
= m_pullup(m
, iphlen
+ sizeof(struct udphdr
))) == 0) {
ip
= mtod(m
, struct ip
*);
uh
= (struct udphdr
*)((caddr_t
)ip
+ iphlen
);
* Make mbuf data length reflect UDP length.
* If not enough data to reflect UDP length, drop.
len
= ntohs((u_short
)uh
->uh_ulen
);
m_adj(m
, len
- ip
->ip_len
);
* Save a copy of the IP header in case we want restore it
* for sending an ICMP error message in response.
* Checksum extended UDP header and data.
if (udpcksum
&& uh
->uh_sum
) {
((struct ipovly
*)ip
)->ih_next
= 0;
((struct ipovly
*)ip
)->ih_prev
= 0;
((struct ipovly
*)ip
)->ih_x1
= 0;
((struct ipovly
*)ip
)->ih_len
= uh
->uh_ulen
;
if (uh
->uh_sum
= in_cksum(m
, len
+ sizeof (struct ip
))) {
if (IN_MULTICAST(ntohl(ip
->ip_dst
.s_addr
)) ||
in_broadcast(ip
->ip_dst
)) {
* 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
= uh
->uh_sport
;
udp_in
.sin_addr
= ip
->ip_src
;
m
->m_len
-= sizeof (struct udpiphdr
);
m
->m_data
+= sizeof (struct udpiphdr
);
* Locate pcb(s) for datagram.
* (Algorithm copied from raw_intr().)
for (inp
= udb
.inp_next
; inp
!= &udb
; inp
= inp
->inp_next
) {
if (inp
->inp_lport
!= uh
->uh_dport
)
if (inp
->inp_laddr
.s_addr
!= INADDR_ANY
) {
if (inp
->inp_laddr
.s_addr
!=
if (inp
->inp_faddr
.s_addr
!= INADDR_ANY
) {
if (inp
->inp_faddr
.s_addr
!=
inp
->inp_fport
!= uh
->uh_sport
)
if ((n
= m_copy(m
, 0, M_COPYALL
)) != NULL
) {
if (sbappendaddr(&last
->so_rcv
,
(struct sockaddr
*)&udp_in
,
n
, (struct mbuf
*)0) == 0)
* Don't look for additional matches if this one
* does not have the SO_REUSEADDR socket option 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_REUSEADDR option after setting it.
if ((last
->so_options
& SO_REUSEADDR
) == 0)
* No matching pcb found; discard datagram.
* (No need to send an ICMP Port Unreachable
* for a broadcast or multicast datgram.)
if (sbappendaddr(&last
->so_rcv
, (struct sockaddr
*)&udp_in
,
m
, (struct mbuf
*)0) == 0)
* Locate pcb for datagram.
if (inp
->inp_lport
!= uh
->uh_dport
||
inp
->inp_fport
!= uh
->uh_sport
||
inp
->inp_faddr
.s_addr
!= ip
->ip_src
.s_addr
||
inp
->inp_laddr
.s_addr
!= ip
->ip_dst
.s_addr
) {
inp
= in_pcblookup(&udb
, ip
->ip_src
, uh
->uh_sport
,
ip
->ip_dst
, uh
->uh_dport
, INPLOOKUP_WILDCARD
);
udpstat
.udpps_pcbcachemiss
++;
/* don't send ICMP response for broadcast packet */
/* XXX why don't we do this with MULTICAST? */
if (m
->m_flags
& (M_BCAST
| M_MCAST
)) {
udpstat
.udps_noportbcast
++;
static struct in_addr fake
;
icmp_error(m
, ICMP_UNREACH
, ICMP_UNREACH_PORT
, fake
, 0);
* Construct sockaddr format source address.
* Stuff source address and datagram in user buffer.
udp_in
.sin_port
= uh
->uh_sport
;
udp_in
.sin_addr
= ip
->ip_src
;
if (inp
->inp_flags
& INP_CONTROLOPTS
) {
struct mbuf
**mp
= &opts
;
struct mbuf
*udp_saveopt();
if (inp
->inp_flags
& INP_RECVDSTADDR
) {
*mp
= udp_saveopt((caddr_t
) &ip
->ip_dst
,
sizeof(struct in_addr
), IP_RECVDSTADDR
);
/* options were tossed above */
if (inp
->inp_flags
& INP_RECVOPTS
) {
*mp
= udp_saveopt((caddr_t
) opts_deleted_above
,
sizeof(struct in_addr
), IP_RECVOPTS
);
/* ip_srcroute doesn't do what we want here, need to fix */
if (inp
->inp_flags
& INP_RECVRETOPTS
) {
*mp
= udp_saveopt((caddr_t
) ip_srcroute(),
sizeof(struct in_addr
), IP_RECVRETOPTS
);
iphlen
+= sizeof(struct udphdr
);
m
->m_pkthdr
.len
-= iphlen
;
if (sbappendaddr(&inp
->inp_socket
->so_rcv
, (struct sockaddr
*)&udp_in
,
sorwakeup(inp
->inp_socket
);
* Create a "control" mbuf containing the specified data
* with the specified type for presentation with a datagram.
udp_saveopt(p
, size
, type
)
register struct cmsghdr
*cp
;
if ((m
= m_get(M_DONTWAIT
, MT_CONTROL
)) == NULL
)
return ((struct mbuf
*) NULL
);
cp
= (struct cmsghdr
*) mtod(m
, struct cmsghdr
*);
bcopy(p
, (caddr_t
)(cp
+ 1), size
);
cp
->cmsg_level
= IPPROTO_IP
;
* Notify a udp user of an asynchronous error;
* just wake up so that he can collect error status.
register struct inpcb
*inp
;
inp
->inp_socket
->so_error
= errno
;
sorwakeup(inp
->inp_socket
);
sowwakeup(inp
->inp_socket
);
udp_ctlinput(cmd
, sa
, ip
)
register struct udphdr
*uh
;
extern struct in_addr zeroin_addr
;
extern u_char inetctlerrmap
[];
if ((unsigned)cmd
> PRC_NCMDS
|| inetctlerrmap
[cmd
] == 0)
uh
= (struct udphdr
*)((caddr_t
)ip
+ (ip
->ip_hl
<< 2));
in_pcbnotify(&udb
, sa
, uh
->uh_dport
, ip
->ip_src
, uh
->uh_sport
,
in_pcbnotify(&udb
, sa
, 0, zeroin_addr
, 0, cmd
, udp_notify
);
udp_output(inp
, m
, addr
, control
)
register struct inpcb
*inp
;
struct mbuf
*addr
, *control
;
register struct udpiphdr
*ui
;
register int len
= m
->m_pkthdr
.len
;
m_freem(control
); /* XXX */
if (inp
->inp_faddr
.s_addr
!= INADDR_ANY
) {
* Must block input while temporarily connected.
error
= in_pcbconnect(inp
, addr
);
if (inp
->inp_faddr
.s_addr
== INADDR_ANY
) {
* Calculate data length and get a mbuf
* for UDP and IP headers.
M_PREPEND(m
, sizeof(struct udpiphdr
), M_WAIT
);
* 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_len
= htons((u_short
)len
+ sizeof (struct udphdr
));
ui
->ui_src
= inp
->inp_laddr
;
ui
->ui_dst
= inp
->inp_faddr
;
ui
->ui_sport
= inp
->inp_lport
;
ui
->ui_dport
= inp
->inp_fport
;
ui
->ui_ulen
= ui
->ui_len
;
* Stuff checksum and output datagram.
if ((ui
->ui_sum
= in_cksum(m
, sizeof (struct udpiphdr
) + len
)) == 0)
((struct ip
*)ui
)->ip_len
= sizeof (struct udpiphdr
) + len
;
((struct ip
*)ui
)->ip_ttl
= inp
->inp_ip
.ip_ttl
; /* XXX */
((struct ip
*)ui
)->ip_tos
= inp
->inp_ip
.ip_tos
; /* XXX */
error
= ip_output(m
, inp
->inp_options
, &inp
->inp_route
,
inp
->inp_socket
->so_options
& (SO_DONTROUTE
| SO_BROADCAST
)
| IP_MULTICASTOPTS
, inp
->inp_moptions
udp_usrreq(so
, req
, m
, addr
, control
)
struct mbuf
*m
, *addr
, *control
;
struct inpcb
*inp
= sotoinpcb(so
);
return (in_control(so
, (int)m
, (caddr_t
)addr
,
(struct ifnet
*)control
));
if (inp
== NULL
&& req
!= PRU_ATTACH
) {
* Note: need to block udp_input while changing
* the udp pcb queue and/or pcb addresses.
error
= in_pcballoc(so
, &udb
);
error
= soreserve(so
, udp_sendspace
, udp_recvspace
);
((struct inpcb
*) so
->so_pcb
)->inp_ip
.ip_ttl
= udp_ttl
;
error
= in_pcbbind(inp
, addr
);
if (inp
->inp_faddr
.s_addr
!= INADDR_ANY
) {
error
= in_pcbconnect(inp
, addr
);
if (inp
->inp_faddr
.s_addr
== INADDR_ANY
) {
inp
->inp_laddr
.s_addr
= INADDR_ANY
;
so
->so_state
&= ~SS_ISCONNECTED
; /* XXX */
return (udp_output(inp
, m
, addr
, control
));
in_setsockaddr(inp
, addr
);
in_setpeeraddr(inp
, addr
);
* stat: don't bother with a blocksize.
return (EOPNOTSUPP
); /* do not free mbuf's */
printf("udp control data unexpectedly retained\n");
if (inp
== udp_last_inpcb
)