Commit ba4e58ec authored by Gerrit Renker's avatar Gerrit Renker Committed by David S. Miller

[NET]: Supporting UDP-Lite (RFC 3828) in Linux

This is a revision of the previously submitted patch, which alters
the way files are organized and compiled in the following manner:

	* UDP and UDP-Lite now use separate object files
	* source file dependencies resolved via header files
	  net/ipv{4,6}/udp_impl.h
	* order of inclusion files in udp.c/udplite.c adapted
	  accordingly

[NET/IPv4]: Support for the UDP-Lite protocol (RFC 3828)

This patch adds support for UDP-Lite to the IPv4 stack, provided as an
extension to the existing UDPv4 code:
        * generic routines are all located in net/ipv4/udp.c
        * UDP-Lite specific routines are in net/ipv4/udplite.c
        * MIB/statistics support in /proc/net/snmp and /proc/net/udplite
        * shared API with extensions for partial checksum coverage

[NET/IPv6]: Extension for UDP-Lite over IPv6

It extends the existing UDPv6 code base with support for UDP-Lite
in the same manner as per UDPv4. In particular,
        * UDPv6 generic and shared code is in net/ipv6/udp.c
        * UDP-Litev6 specific extensions are in net/ipv6/udplite.c
        * MIB/statistics support in /proc/net/snmp6 and /proc/net/udplite6
        * support for IPV6_ADDRFORM
        * aligned the coding style of protocol initialisation with af_inet6.c
        * made the error handling in udpv6_queue_rcv_skb consistent;
          to return `-1' on error on all error cases
        * consolidation of shared code

[NET]: UDP-Lite Documentation and basic XFRM/Netfilter support

The UDP-Lite patch further provides
        * API documentation for UDP-Lite
        * basic xfrm support
        * basic netfilter support for IPv4 and IPv6 (LOG target)
Signed-off-by: default avatarGerrit Renker <gerrit@erg.abdn.ac.uk>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6051e2f4
This diff is collapsed.
......@@ -45,6 +45,7 @@ enum {
IPPROTO_COMP = 108, /* Compression Header protocol */
IPPROTO_SCTP = 132, /* Stream Control Transport Protocol */
IPPROTO_UDPLITE = 136, /* UDP-Lite (RFC 3828) */
IPPROTO_RAW = 255, /* Raw IP packets */
IPPROTO_MAX
......
......@@ -264,6 +264,7 @@ struct ucred {
#define SOL_IPV6 41
#define SOL_ICMPV6 58
#define SOL_SCTP 132
#define SOL_UDPLITE 136 /* UDP-Lite (RFC 3828) */
#define SOL_RAW 255
#define SOL_IPX 256
#define SOL_AX25 257
......
......@@ -38,6 +38,7 @@ struct udphdr {
#include <linux/types.h>
#include <net/inet_sock.h>
#define UDP_HTABLE_SIZE 128
struct udp_sock {
/* inet_sock has to be the first member */
......@@ -50,12 +51,23 @@ struct udp_sock {
* when the socket is uncorked.
*/
__u16 len; /* total length of pending frames */
/*
* Fields specific to UDP-Lite.
*/
__u16 pcslen;
__u16 pcrlen;
/* indicator bits used by pcflag: */
#define UDPLITE_BIT 0x1 /* set by udplite proto init function */
#define UDPLITE_SEND_CC 0x2 /* set via udplite setsockopt */
#define UDPLITE_RECV_CC 0x4 /* set via udplite setsocktopt */
__u8 pcflag; /* marks socket as UDP-Lite if > 0 */
};
static inline struct udp_sock *udp_sk(const struct sock *sk)
{
return (struct udp_sock *)sk;
}
#define IS_UDPLITE(__sk) (udp_sk(__sk)->pcflag)
#endif
......
......@@ -158,9 +158,13 @@ DECLARE_SNMP_STAT(struct icmpv6_mib, icmpv6_statistics);
SNMP_INC_STATS_OFFSET_BH(icmpv6_statistics, field, _offset); \
})
DECLARE_SNMP_STAT(struct udp_mib, udp_stats_in6);
#define UDP6_INC_STATS(field) SNMP_INC_STATS(udp_stats_in6, field)
#define UDP6_INC_STATS_BH(field) SNMP_INC_STATS_BH(udp_stats_in6, field)
#define UDP6_INC_STATS_USER(field) SNMP_INC_STATS_USER(udp_stats_in6, field)
DECLARE_SNMP_STAT(struct udp_mib, udplite_stats_in6);
#define UDP6_INC_STATS_BH(field, is_udplite) do { \
if (is_udplite) SNMP_INC_STATS_BH(udplite_stats_in6, field); \
else SNMP_INC_STATS_BH(udp_stats_in6, field); } while(0)
#define UDP6_INC_STATS_USER(field, is_udplite) do { \
if (is_udplite) SNMP_INC_STATS_USER(udplite_stats_in6, field); \
else SNMP_INC_STATS_USER(udp_stats_in6, field); } while(0)
int snmp6_register_dev(struct inet6_dev *idev);
int snmp6_unregister_dev(struct inet6_dev *idev);
......@@ -604,6 +608,8 @@ extern int tcp6_proc_init(void);
extern void tcp6_proc_exit(void);
extern int udp6_proc_init(void);
extern void udp6_proc_exit(void);
extern int udplite6_proc_init(void);
extern void udplite6_proc_exit(void);
extern int ipv6_misc_proc_init(void);
extern void ipv6_misc_proc_exit(void);
......
......@@ -11,6 +11,7 @@
extern struct proto rawv6_prot;
extern struct proto udpv6_prot;
extern struct proto udplitev6_prot;
extern struct proto tcpv6_prot;
struct flowi;
......@@ -24,6 +25,7 @@ extern void ipv6_destopt_init(void);
/* transport protocols */
extern void rawv6_init(void);
extern void udpv6_init(void);
extern void udplitev6_init(void);
extern void tcpv6_init(void);
extern int udpv6_connect(struct sock *sk,
......
......@@ -26,9 +26,28 @@
#include <net/inet_sock.h>
#include <net/sock.h>
#include <net/snmp.h>
#include <net/ip.h>
#include <linux/ipv6.h>
#include <linux/seq_file.h>
#define UDP_HTABLE_SIZE 128
/**
* struct udp_skb_cb - UDP(-Lite) private variables
*
* @header: private variables used by IPv4/IPv6
* @cscov: checksum coverage length (UDP-Lite only)
* @partial_cov: if set indicates partial csum coverage
*/
struct udp_skb_cb {
union {
struct inet_skb_parm h4;
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
struct inet6_skb_parm h6;
#endif
} header;
__u16 cscov;
__u8 partial_cov;
};
#define UDP_SKB_CB(__skb) ((struct udp_skb_cb *)((__skb)->cb))
extern struct hlist_head udp_hash[UDP_HTABLE_SIZE];
extern rwlock_t udp_hash_lock;
......@@ -47,6 +66,62 @@ extern struct proto udp_prot;
struct sk_buff;
/*
* Generic checksumming routines for UDP(-Lite) v4 and v6
*/
static inline u16 __udp_lib_checksum_complete(struct sk_buff *skb)
{
if (! UDP_SKB_CB(skb)->partial_cov)
return __skb_checksum_complete(skb);
return csum_fold(skb_checksum(skb, 0, UDP_SKB_CB(skb)->cscov,
skb->csum));
}
static __inline__ int udp_lib_checksum_complete(struct sk_buff *skb)
{
return skb->ip_summed != CHECKSUM_UNNECESSARY &&
__udp_lib_checksum_complete(skb);
}
/**
* udp_csum_outgoing - compute UDPv4/v6 checksum over fragments
* @sk: socket we are writing to
* @skb: sk_buff containing the filled-in UDP header
* (checksum field must be zeroed out)
*/
static inline u32 udp_csum_outgoing(struct sock *sk, struct sk_buff *skb)
{
u32 csum = csum_partial(skb->h.raw, sizeof(struct udphdr), 0);
skb_queue_walk(&sk->sk_write_queue, skb) {
csum = csum_add(csum, skb->csum);
}
return csum;
}
/* hash routines shared between UDPv4/6 and UDP-Litev4/6 */
static inline void udp_lib_hash(struct sock *sk)
{
BUG();
}
static inline void udp_lib_unhash(struct sock *sk)
{
write_lock_bh(&udp_hash_lock);
if (sk_del_node_init(sk)) {
inet_sk(sk)->num = 0;
sock_prot_dec_use(sk->sk_prot);
}
write_unlock_bh(&udp_hash_lock);
}
static inline void udp_lib_close(struct sock *sk, long timeout)
{
sk_common_release(sk);
}
/* net/ipv4/udp.c */
extern int udp_get_port(struct sock *sk, unsigned short snum,
int (*saddr_cmp)(const struct sock *, const struct sock *));
extern void udp_err(struct sk_buff *, u32);
......@@ -61,21 +136,29 @@ extern unsigned int udp_poll(struct file *file, struct socket *sock,
poll_table *wait);
DECLARE_SNMP_STAT(struct udp_mib, udp_statistics);
#define UDP_INC_STATS(field) SNMP_INC_STATS(udp_statistics, field)
#define UDP_INC_STATS_BH(field) SNMP_INC_STATS_BH(udp_statistics, field)
#define UDP_INC_STATS_USER(field) SNMP_INC_STATS_USER(udp_statistics, field)
/*
* SNMP statistics for UDP and UDP-Lite
*/
#define UDP_INC_STATS_USER(field, is_udplite) do { \
if (is_udplite) SNMP_INC_STATS_USER(udplite_statistics, field); \
else SNMP_INC_STATS_USER(udp_statistics, field); } while(0)
#define UDP_INC_STATS_BH(field, is_udplite) do { \
if (is_udplite) SNMP_INC_STATS_BH(udplite_statistics, field); \
else SNMP_INC_STATS_BH(udp_statistics, field); } while(0)
/* /proc */
struct udp_seq_afinfo {
struct module *owner;
char *name;
sa_family_t family;
struct hlist_head *hashtable;
int (*seq_show) (struct seq_file *m, void *v);
struct file_operations *seq_fops;
};
struct udp_iter_state {
sa_family_t family;
struct hlist_head *hashtable;
int bucket;
struct seq_operations seq_ops;
};
......
/*
* Definitions for the UDP-Lite (RFC 3828) code.
*/
#ifndef _UDPLITE_H
#define _UDPLITE_H
/* UDP-Lite socket options */
#define UDPLITE_SEND_CSCOV 10 /* sender partial coverage (as sent) */
#define UDPLITE_RECV_CSCOV 11 /* receiver partial coverage (threshold ) */
extern struct proto udplite_prot;
extern struct hlist_head udplite_hash[UDP_HTABLE_SIZE];
/* UDP-Lite does not have a standardized MIB yet, so we inherit from UDP */
DECLARE_SNMP_STAT(struct udp_mib, udplite_statistics);
/*
* Checksum computation is all in software, hence simpler getfrag.
*/
static __inline__ int udplite_getfrag(void *from, char *to, int offset,
int len, int odd, struct sk_buff *skb)
{
return memcpy_fromiovecend(to, (struct iovec *) from, offset, len);
}
/* Designate sk as UDP-Lite socket */
static inline int udplite_sk_init(struct sock *sk)
{
udp_sk(sk)->pcflag = UDPLITE_BIT;
return 0;
}
/*
* Checksumming routines
*/
static inline int udplite_checksum_init(struct sk_buff *skb, struct udphdr *uh)
{
u16 cscov;
/* In UDPv4 a zero checksum means that the transmitter generated no
* checksum. UDP-Lite (like IPv6) mandates checksums, hence packets
* with a zero checksum field are illegal. */
if (uh->check == 0) {
LIMIT_NETDEBUG(KERN_DEBUG "UDPLITE: zeroed checksum field\n");
return 1;
}
UDP_SKB_CB(skb)->partial_cov = 0;
cscov = ntohs(uh->len);
if (cscov == 0) /* Indicates that full coverage is required. */
cscov = skb->len;
else if (cscov < 8 || cscov > skb->len) {
/*
* Coverage length violates RFC 3828: log and discard silently.
*/
LIMIT_NETDEBUG(KERN_DEBUG "UDPLITE: bad csum coverage %d/%d\n",
cscov, skb->len);
return 1;
} else if (cscov < skb->len)
UDP_SKB_CB(skb)->partial_cov = 1;
UDP_SKB_CB(skb)->cscov = cscov;
/*
* There is no known NIC manufacturer supporting UDP-Lite yet,
* hence ip_summed is always (re-)set to CHECKSUM_NONE.
*/
skb->ip_summed = CHECKSUM_NONE;
return 0;
}
static __inline__ int udplite4_csum_init(struct sk_buff *skb, struct udphdr *uh)
{
int rc = udplite_checksum_init(skb, uh);
if (!rc)
skb->csum = csum_tcpudp_nofold(skb->nh.iph->saddr,
skb->nh.iph->daddr,
skb->len, IPPROTO_UDPLITE, 0);
return rc;
}
static __inline__ int udplite6_csum_init(struct sk_buff *skb, struct udphdr *uh)
{
int rc = udplite_checksum_init(skb, uh);
if (!rc)
skb->csum = ~csum_ipv6_magic(&skb->nh.ipv6h->saddr,
&skb->nh.ipv6h->daddr,
skb->len, IPPROTO_UDPLITE, 0);
return rc;
}
static inline int udplite_sender_cscov(struct udp_sock *up, struct udphdr *uh)
{
int cscov = up->len;
/*
* Sender has set `partial coverage' option on UDP-Lite socket
*/
if (up->pcflag & UDPLITE_SEND_CC) {
if (up->pcslen < up->len) {
/* up->pcslen == 0 means that full coverage is required,
* partial coverage only if 0 < up->pcslen < up->len */
if (0 < up->pcslen) {
cscov = up->pcslen;
}
uh->len = htons(up->pcslen);
}
/*
* NOTE: Causes for the error case `up->pcslen > up->len':
* (i) Application error (will not be penalized).
* (ii) Payload too big for send buffer: data is split
* into several packets, each with its own header.
* In this case (e.g. last segment), coverage may
* exceed packet length.
* Since packets with coverage length > packet length are
* illegal, we fall back to the defaults here.
*/
}
return cscov;
}
static inline u32 udplite_csum_outgoing(struct sock *sk, struct sk_buff *skb)
{
u32 csum = 0;
int off, len, cscov = udplite_sender_cscov(udp_sk(sk), skb->h.uh);
skb->ip_summed = CHECKSUM_NONE; /* no HW support for checksumming */
skb_queue_walk(&sk->sk_write_queue, skb) {
off = skb->h.raw - skb->data;
len = skb->len - off;
csum = skb_checksum(skb, off, (cscov > len)? len : cscov, csum);
if ((cscov -= len) <= 0)
break;
}
return csum;
}
extern void udplite4_register(void);
extern int udplite_get_port(struct sock *sk, unsigned short snum,
int (*scmp)(const struct sock *, const struct sock *));
#endif /* _UDPLITE_H */
......@@ -468,6 +468,7 @@ __be16 xfrm_flowi_sport(struct flowi *fl)
switch(fl->proto) {
case IPPROTO_TCP:
case IPPROTO_UDP:
case IPPROTO_UDPLITE:
case IPPROTO_SCTP:
port = fl->fl_ip_sport;
break;
......@@ -493,6 +494,7 @@ __be16 xfrm_flowi_dport(struct flowi *fl)
switch(fl->proto) {
case IPPROTO_TCP:
case IPPROTO_UDP:
case IPPROTO_UDPLITE:
case IPPROTO_SCTP:
port = fl->fl_ip_dport;
break;
......
......@@ -8,7 +8,8 @@ obj-y := route.o inetpeer.o protocol.o \
inet_timewait_sock.o inet_connection_sock.o \
tcp.o tcp_input.o tcp_output.o tcp_timer.o tcp_ipv4.o \
tcp_minisocks.o tcp_cong.o \
datagram.o raw.o udp.o arp.o icmp.o devinet.o af_inet.o igmp.o \
datagram.o raw.o udp.o udplite.o \
arp.o icmp.o devinet.o af_inet.o igmp.o \
sysctl_net_ipv4.o fib_frontend.o fib_semantics.o
obj-$(CONFIG_IP_FIB_HASH) += fib_hash.o
......
......@@ -104,6 +104,7 @@
#include <net/inet_connection_sock.h>
#include <net/tcp.h>
#include <net/udp.h>
#include <net/udplite.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/raw.h>
......@@ -1223,10 +1224,13 @@ static int __init init_ipv4_mibs(void)
tcp_statistics[1] = alloc_percpu(struct tcp_mib);
udp_statistics[0] = alloc_percpu(struct udp_mib);
udp_statistics[1] = alloc_percpu(struct udp_mib);
udplite_statistics[0] = alloc_percpu(struct udp_mib);
udplite_statistics[1] = alloc_percpu(struct udp_mib);
if (!
(net_statistics[0] && net_statistics[1] && ip_statistics[0]
&& ip_statistics[1] && tcp_statistics[0] && tcp_statistics[1]
&& udp_statistics[0] && udp_statistics[1]))
&& udp_statistics[0] && udp_statistics[1]
&& udplite_statistics[0] && udplite_statistics[1] ) )
return -ENOMEM;
(void) tcp_mib_init();
......@@ -1313,6 +1317,8 @@ static int __init inet_init(void)
/* Setup TCP slab cache for open requests. */
tcp_init();
/* Add UDP-Lite (RFC 3828) */
udplite4_register();
/*
* Set the ICMP layer up
......
......@@ -171,11 +171,15 @@ static void dump_packet(const struct nf_loginfo *info,
}
break;
}
case IPPROTO_UDP: {
case IPPROTO_UDP:
case IPPROTO_UDPLITE: {
struct udphdr _udph, *uh;
/* Max length: 10 "PROTO=UDP " */
printk("PROTO=UDP ");
if (ih->protocol == IPPROTO_UDP)
/* Max length: 10 "PROTO=UDP " */
printk("PROTO=UDP " );
else /* Max length: 14 "PROTO=UDPLITE " */
printk("PROTO=UDPLITE ");
if (ntohs(ih->frag_off) & IP_OFFSET)
break;
......@@ -341,6 +345,7 @@ static void dump_packet(const struct nf_loginfo *info,
/* IP: 40+46+6+11+127 = 230 */
/* TCP: 10+max(25,20+30+13+9+32+11+127) = 252 */
/* UDP: 10+max(25,20) = 35 */
/* UDPLITE: 14+max(25,20) = 39 */
/* ICMP: 11+max(25, 18+25+max(19,14,24+3+n+10,3+n+10)) = 91+n */
/* ESP: 10+max(25)+15 = 50 */
/* AH: 9+max(25)+15 = 49 */
......
......@@ -38,6 +38,7 @@
#include <net/protocol.h>
#include <net/tcp.h>
#include <net/udp.h>
#include <net/udplite.h>
#include <linux/inetdevice.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
......@@ -66,6 +67,7 @@ static int sockstat_seq_show(struct seq_file *seq, void *v)
tcp_death_row.tw_count, atomic_read(&tcp_sockets_allocated),
atomic_read(&tcp_memory_allocated));
seq_printf(seq, "UDP: inuse %d\n", fold_prot_inuse(&udp_prot));
seq_printf(seq, "UDPLITE: inuse %d\n", fold_prot_inuse(&udplite_prot));
seq_printf(seq, "RAW: inuse %d\n", fold_prot_inuse(&raw_prot));
seq_printf(seq, "FRAG: inuse %d memory %d\n", ip_frag_nqueues,
atomic_read(&ip_frag_mem));
......@@ -304,6 +306,17 @@ static int snmp_seq_show(struct seq_file *seq, void *v)
fold_field((void **) udp_statistics,
snmp4_udp_list[i].entry));
/* the UDP and UDP-Lite MIBs are the same */
seq_puts(seq, "\nUdpLite:");
for (i = 0; snmp4_udp_list[i].name != NULL; i++)
seq_printf(seq, " %s", snmp4_udp_list[i].name);
seq_puts(seq, "\nUdpLite:");
for (i = 0; snmp4_udp_list[i].name != NULL; i++)
seq_printf(seq, " %lu",
fold_field((void **) udplite_statistics,
snmp4_udp_list[i].entry) );
seq_putc(seq, '\n');
return 0;
}
......
This diff is collapsed.
#ifndef _UDP4_IMPL_H
#define _UDP4_IMPL_H
#include <net/udp.h>
#include <net/udplite.h>
#include <net/protocol.h>
#include <net/inet_common.h>
extern int __udp4_lib_rcv(struct sk_buff *, struct hlist_head [], int );
extern void __udp4_lib_err(struct sk_buff *, u32, struct hlist_head []);
extern int __udp_lib_get_port(struct sock *sk, unsigned short snum,
struct hlist_head udptable[], int *port_rover,
int (*)(const struct sock*,const struct sock*));
extern int ipv4_rcv_saddr_equal(const struct sock *, const struct sock *);
extern int udp_setsockopt(struct sock *sk, int level, int optname,
char __user *optval, int optlen);
extern int udp_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen);
#ifdef CONFIG_COMPAT
extern int compat_udp_setsockopt(struct sock *sk, int level, int optname,
char __user *optval, int optlen);
extern int compat_udp_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen);
#endif
extern int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len, int noblock, int flags, int *addr_len);
extern int udp_sendpage(struct sock *sk, struct page *page, int offset,
size_t size, int flags);
extern int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb);
extern int udp_destroy_sock(struct sock *sk);
#ifdef CONFIG_PROC_FS
extern int udp4_seq_show(struct seq_file *seq, void *v);
#endif
#endif /* _UDP4_IMPL_H */
/*
* UDPLITE An implementation of the UDP-Lite protocol (RFC 3828).
*
* Version: $Id: udplite.c,v 1.25 2006/10/19 07:22:36 gerrit Exp $
*
* Authors: Gerrit Renker <gerrit@erg.abdn.ac.uk>
*
* Changes:
* Fixes:
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include "udp_impl.h"
DEFINE_SNMP_STAT(struct udp_mib, udplite_statistics) __read_mostly;
struct hlist_head udplite_hash[UDP_HTABLE_SIZE];
static int udplite_port_rover;
__inline__ int udplite_get_port(struct sock *sk, unsigned short p,
int (*c)(const struct sock *, const struct sock *))
{
return __udp_lib_get_port(sk, p, udplite_hash, &udplite_port_rover, c);
}
static __inline__ int udplite_v4_get_port(struct sock *sk, unsigned short snum)
{
return udplite_get_port(sk, snum, ipv4_rcv_saddr_equal);
}
__inline__ int udplite_rcv(struct sk_buff *skb)
{
return __udp4_lib_rcv(skb, udplite_hash, 1);
}
__inline__ void udplite_err(struct sk_buff *skb, u32 info)
{
return __udp4_lib_err(skb, info, udplite_hash);
}
static struct net_protocol udplite_protocol = {
.handler = udplite_rcv,
.err_handler = udplite_err,
.no_policy = 1,
};
struct proto udplite_prot = {
.name = "UDP-Lite",
.owner = THIS_MODULE,
.close = udp_lib_close,
.connect = ip4_datagram_connect,
.disconnect = udp_disconnect,
.ioctl = udp_ioctl,
.init = udplite_sk_init,
.destroy = udp_destroy_sock,
.setsockopt = udp_setsockopt,
.getsockopt = udp_getsockopt,
.sendmsg = udp_sendmsg,
.recvmsg = udp_recvmsg,
.sendpage = udp_sendpage,
.backlog_rcv = udp_queue_rcv_skb,
.hash = udp_lib_hash,
.unhash = udp_lib_unhash,
.get_port = udplite_v4_get_port,
.obj_size = sizeof(struct udp_sock),
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_udp_setsockopt,
.compat_getsockopt = compat_udp_getsockopt,
#endif
};
static struct inet_protosw udplite4_protosw = {
.type = SOCK_DGRAM,
.protocol = IPPROTO_UDPLITE,
.prot = &udplite_prot,
.ops = &inet_dgram_ops,
.capability = -1,
.no_check = 0, /* must checksum (RFC 3828) */
.flags = INET_PROTOSW_PERMANENT,
};
#ifdef CONFIG_PROC_FS
static struct file_operations udplite4_seq_fops;
static struct udp_seq_afinfo udplite4_seq_afinfo = {
.owner = THIS_MODULE,
.name = "udplite",
.family = AF_INET,
.hashtable = udplite_hash,
.seq_show = udp4_seq_show,
.seq_fops = &udplite4_seq_fops,
};
#endif
void __init udplite4_register(void)
{
if (proto_register(&udplite_prot, 1))
goto out_register_err;
if (inet_add_protocol(&udplite_protocol, IPPROTO_UDPLITE) < 0)
goto out_unregister_proto;
inet_register_protosw(&udplite4_protosw);
#ifdef CONFIG_PROC_FS
if (udp_proc_register(&udplite4_seq_afinfo)) /* udplite4_proc_init() */
printk(KERN_ERR "%s: Cannot register /proc!\n", __FUNCTION__);
#endif
return;
out_unregister_proto:
proto_unregister(&udplite_prot);
out_register_err:
printk(KERN_CRIT "%s: Cannot add UDP-Lite protocol.\n", __FUNCTION__);
}
EXPORT_SYMBOL(udplite_hash);
EXPORT_SYMBOL(udplite_prot);
EXPORT_SYMBOL(udplite_get_port);
......@@ -199,6 +199,7 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl)
if (!(iph->frag_off & htons(IP_MF | IP_OFFSET))) {
switch (iph->protocol) {
case IPPROTO_UDP:
case IPPROTO_UDPLITE:
case IPPROTO_TCP:
case IPPROTO_SCTP:
case IPPROTO_DCCP:
......
......@@ -5,8 +5,8 @@
obj-$(CONFIG_IPV6) += ipv6.o
ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \
route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o raw.o \
protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \
raw.o protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \
ip6_flowlabel.o ipv6_syms.o inet6_connection_sock.o
......
......@@ -49,6 +49,7 @@
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/udp.h>
#include <net/udplite.h>
#include <net/tcp.h>
#include <net/ipip.h>
#include <net/protocol.h>
......@@ -737,8 +738,13 @@ static int __init init_ipv6_mibs(void)
if (snmp6_mib_init((void **)udp_stats_in6, sizeof (struct udp_mib),
__alignof__(struct udp_mib)) < 0)
goto err_udp_mib;
if (snmp6_mib_init((void **)udplite_stats_in6, sizeof (struct udp_mib),
__alignof__(struct udp_mib)) < 0)
goto err_udplite_mib;
return 0;
err_udplite_mib:
snmp6_mib_free((void **)udp_stats_in6);
err_udp_mib:
snmp6_mib_free((void **)icmpv6_statistics);
err_icmp_mib:
......@@ -753,6 +759,7 @@ static void cleanup_ipv6_mibs(void)
snmp6_mib_free((void **)ipv6_statistics);
snmp6_mib_free((void **)icmpv6_statistics);
snmp6_mib_free((void **)udp_stats_in6);
snmp6_mib_free((void **)udplite_stats_in6);
}
static int __init inet6_init(void)
......@@ -780,10 +787,14 @@ static int __init inet6_init(void)
if (err)
goto out_unregister_tcp_proto;
err = proto_register(&rawv6_prot, 1);
err = proto_register(&udplitev6_prot, 1);
if (err)
goto out_unregister_udp_proto;
err = proto_register(&rawv6_prot, 1);
if (err)
goto out_unregister_udplite_proto;
/* Register the socket-side information for inet6_create. */
for(r = &inetsw6[0]; r < &inetsw6[SOCK_MAX]; ++r)
......@@ -837,6 +848,8 @@ static int __init inet6_init(void)
goto proc_tcp6_fail;
if (udp6_proc_init())
goto proc_udp6_fail;
if (udplite6_proc_init())
goto proc_udplite6_fail;
if (ipv6_misc_proc_init())
goto proc_misc6_fail;
......@@ -862,6 +875,7 @@ static int __init inet6_init(void)
/* Init v6 transport protocols. */
udpv6_init();
udplitev6_init();
tcpv6_init();
ipv6_packet_init();
......@@ -879,6 +893,8 @@ static int __init inet6_init(void)
proc_anycast6_fail:
ipv6_misc_proc_exit();
proc_misc6_fail:
udplite6_proc_exit();
proc_udplite6_fail:
udp6_proc_exit();
proc_udp6_fail:
tcp6_proc_exit();
......@@ -902,6 +918,8 @@ static int __init inet6_init(void)
sock_unregister(PF_INET6);
out_unregister_raw_proto:
proto_unregister(&rawv6_prot);
out_unregister_udplite_proto:
proto_unregister(&udplitev6_prot);
out_unregister_udp_proto:
proto_unregister(&udpv6_prot);
out_unregister_tcp_proto:
......@@ -919,6 +937,7 @@ static void __exit inet6_exit(void)
ac6_proc_exit();
ipv6_misc_proc_exit();
udp6_proc_exit();
udplite6_proc_exit();
tcp6_proc_exit();
raw6_proc_exit();
#endif
......
......@@ -51,6 +51,7 @@
#include <net/inet_common.h>
#include <net/tcp.h>
#include <net/udp.h>
#include <net/udplite.h>
#include <net/xfrm.h>
#include <asm/uaccess.h>
......@@ -239,6 +240,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
struct sk_buff *pktopt;
if (sk->sk_protocol != IPPROTO_UDP &&
sk->sk_protocol != IPPROTO_UDPLITE &&
sk->sk_protocol != IPPROTO_TCP)
break;
......@@ -276,11 +278,15 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
sk->sk_family = PF_INET;
tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
} else {
struct proto *prot = &udp_prot;
if (sk->sk_protocol == IPPROTO_UDPLITE)
prot = &udplite_prot;
local_bh_disable();
sock_prot_dec_use(sk->sk_prot);
sock_prot_inc_use(&udp_prot);
sock_prot_inc_use(prot);
local_bh_enable();
sk->sk_prot = &udp_prot;
sk->sk_prot = prot;
sk->sk_socket->ops = &inet_dgram_ops;
sk->sk_family = PF_INET;
}
......@@ -813,6 +819,7 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
switch (optname) {
case IPV6_ADDRFORM:
if (sk->sk_protocol != IPPROTO_UDP &&
sk->sk_protocol != IPPROTO_UDPLITE &&
sk->sk_protocol != IPPROTO_TCP)
return -EINVAL;
if (sk->sk_state != TCP_ESTABLISHED)
......
......@@ -270,11 +270,15 @@ static void dump_packet(const struct nf_loginfo *info,
}
break;
}
case IPPROTO_UDP: {
case IPPROTO_UDP:
case IPPROTO_UDPLITE: {
struct udphdr _udph, *uh;
/* Max length: 10 "PROTO=UDP " */
printk("PROTO=UDP ");
if (currenthdr == IPPROTO_UDP)
/* Max length: 10 "PROTO=UDP " */
printk("PROTO=UDP " );
else /* Max length: 14 "PROTO=UDPLITE " */
printk("PROTO=UDPLITE ");
if (fragment)
break;
......
......@@ -49,6 +49,8 @@ static int sockstat6_seq_show(struct seq_file *seq, void *v)
fold_prot_inuse(&tcpv6_prot));
seq_printf(seq, "UDP6: inuse %d\n",
fold_prot_inuse(&udpv6_prot));
seq_printf(seq, "UDPLITE6: inuse %d\n",
fold_prot_inuse(&udplitev6_prot));
seq_printf(seq, "RAW6: inuse %d\n",
fold_prot_inuse(&rawv6_prot));
seq_printf(seq, "FRAG6: inuse %d memory %d\n",
......@@ -133,6 +135,14 @@ static struct snmp_mib snmp6_udp6_list[] = {
SNMP_MIB_SENTINEL
};
static struct snmp_mib snmp6_udplite6_list[] = {
SNMP_MIB_ITEM("UdpLite6InDatagrams", UDP_MIB_INDATAGRAMS),
SNMP_MIB_ITEM("UdpLite6NoPorts", UDP_MIB_NOPORTS),
SNMP_MIB_ITEM("UdpLite6InErrors", UDP_MIB_INERRORS),
SNMP_MIB_ITEM("UdpLite6OutDatagrams", UDP_MIB_OUTDATAGRAMS),
SNMP_MIB_SENTINEL
};
static unsigned long
fold_field(void *mib[], int offt)
{
......@@ -167,6 +177,7 @@ static int snmp6_seq_show(struct seq_file *seq, void *v)
snmp6_seq_show_item(seq, (void **)ipv6_statistics, snmp6_ipstats_list);
snmp6_seq_show_item(seq, (void **)icmpv6_statistics, snmp6_icmp6_list);
snmp6_seq_show_item(seq, (void **)udp_stats_in6, snmp6_udp6_list);
snmp6_seq_show_item(seq, (void **)udplite_stats_in6, snmp6_udplite6_list);
}
return 0;
}
......
This diff is collapsed.
#ifndef _UDP6_IMPL_H
#define _UDP6_IMPL_H
#include <net/udp.h>
#include <net/udplite.h>
#include <net/protocol.h>
#include <net/addrconf.h>
#include <net/inet_common.h>
extern int __udp6_lib_rcv(struct sk_buff **, struct hlist_head [], int );
extern void __udp6_lib_err(struct sk_buff *, struct inet6_skb_parm *,
int , int , int , __be32 , struct hlist_head []);
extern int udpv6_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen);
extern int udpv6_setsockopt(struct sock *sk, int level, int optname,
char __user *optval, int optlen);
#ifdef CONFIG_COMPAT
extern int compat_udpv6_setsockopt(struct sock *sk, int level, int optname,
char __user *optval, int optlen);
extern int compat_udpv6_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen);
#endif
extern int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
struct msghdr *msg, size_t len);
extern int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk,
struct msghdr *msg, size_t len,
int noblock, int flags, int *addr_len);
extern int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb);
extern int udpv6_destroy_sock(struct sock *sk);
#ifdef CONFIG_PROC_FS
extern int udp6_seq_show(struct seq_file *seq, void *v);
#endif
#endif /* _UDP6_IMPL_H */
/*
* UDPLITEv6 An implementation of the UDP-Lite protocol over IPv6.
* See also net/ipv4/udplite.c
*
* Version: $Id: udplite.c,v 1.9 2006/10/19 08:28:10 gerrit Exp $
*
* Authors: Gerrit Renker <gerrit@erg.abdn.ac.uk>
*
* Changes:
* Fixes:
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include "udp_impl.h"
DEFINE_SNMP_STAT(struct udp_mib, udplite_stats_in6) __read_mostly;
static __inline__ int udplitev6_rcv(struct sk_buff **pskb)
{
return __udp6_lib_rcv(pskb, udplite_hash, 1);
}
static __inline__ void udplitev6_err(struct sk_buff *skb,
struct inet6_skb_parm *opt,
int type, int code, int offset, __u32 info)
{
return __udp6_lib_err(skb, opt, type, code, offset, info, udplite_hash);
}
static struct inet6_protocol udplitev6_protocol = {
.handler = udplitev6_rcv,
.err_handler = udplitev6_err,
.flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
};
static __inline__ int udplite_v6_get_port(struct sock *sk, unsigned short snum)
{
return udplite_get_port(sk, snum, ipv6_rcv_saddr_equal);
}
struct proto udplitev6_prot = {
.name = "UDPLITEv6",
.owner = THIS_MODULE,
.close = udp_lib_close,
.connect = ip6_datagram_connect,
.disconnect = udp_disconnect,
.ioctl = udp_ioctl,
.init = udplite_sk_init,
.destroy = udpv6_destroy_sock,
.setsockopt = udpv6_setsockopt,
.getsockopt = udpv6_getsockopt,
.sendmsg = udpv6_sendmsg,
.recvmsg = udpv6_recvmsg,
.backlog_rcv = udpv6_queue_rcv_skb,
.hash = udp_lib_hash,
.unhash = udp_lib_unhash,
.get_port = udplite_v6_get_port,
.obj_size = sizeof(struct udp6_sock),
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_udpv6_setsockopt,
.compat_getsockopt = compat_udpv6_getsockopt,
#endif
};
static struct inet_protosw udplite6_protosw = {
.type = SOCK_DGRAM,
.protocol = IPPROTO_UDPLITE,
.prot = &udplitev6_prot,
.ops = &inet6_dgram_ops,
.capability = -1,
.no_check = 0,
.flags = INET_PROTOSW_PERMANENT,
};
void __init udplitev6_init(void)
{
if (inet6_add_protocol(&udplitev6_protocol, IPPROTO_UDPLITE) < 0)
printk(KERN_ERR "%s: Could not register.\n", __FUNCTION__);
inet6_register_protosw(&udplite6_protosw);
}
#ifdef CONFIG_PROC_FS
static struct file_operations udplite6_seq_fops;
static struct udp_seq_afinfo udplite6_seq_afinfo = {
.owner = THIS_MODULE,
.name = "udplite6",
.family = AF_INET6,
.hashtable = udplite_hash,
.seq_show = udp6_seq_show,
.seq_fops = &udplite6_seq_fops,
};
int __init udplite6_proc_init(void)
{
return udp_proc_register(&udplite6_seq_afinfo);
}
void udplite6_proc_exit(void)
{
udp_proc_unregister(&udplite6_seq_afinfo);
}
#endif
......@@ -274,6 +274,7 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl)
break;
case IPPROTO_UDP:
case IPPROTO_UDPLITE:
case IPPROTO_TCP:
case IPPROTO_SCTP:
case IPPROTO_DCCP:
......
/* Kernel module to match one of a list of TCP/UDP/SCTP/DCCP ports: ports are in
the same place so we can treat them as equal. */
/* Kernel module to match one of a list of TCP/UDP(-Lite)/SCTP/DCCP ports:
ports are in the same place so we can treat them as equal. */
/* (C) 1999-2001 Paul `Rusty' Russell
* (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
......@@ -162,6 +162,7 @@ check(u_int16_t proto,
{
/* Must specify supported protocol, no unknown flags or bad count */
return (proto == IPPROTO_TCP || proto == IPPROTO_UDP
|| proto == IPPROTO_UDPLITE
|| proto == IPPROTO_SCTP || proto == IPPROTO_DCCP)
&& !(ip_invflags & XT_INV_PROTO)
&& (match_flags == XT_MULTIPORT_SOURCE
......
......@@ -10,7 +10,7 @@
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv6/ip6_tables.h>
MODULE_DESCRIPTION("x_tables match for TCP and UDP, supports IPv4 and IPv6");
MODULE_DESCRIPTION("x_tables match for TCP and UDP(-Lite), supports IPv4 and IPv6");
MODULE_LICENSE("GPL");
MODULE_ALIAS("xt_tcp");
MODULE_ALIAS("xt_udp");
......@@ -234,6 +234,24 @@ static struct xt_match xt_tcpudp_match[] = {
.proto = IPPROTO_UDP,
.me = THIS_MODULE,
},
{
.name = "udplite",
.family = AF_INET,
.checkentry = udp_checkentry,
.match = udp_match,
.matchsize = sizeof(struct xt_udp),
.proto = IPPROTO_UDPLITE,
.me = THIS_MODULE,
},
{
.name = "udplite",
.family = AF_INET6,
.checkentry = udp_checkentry,
.match = udp_match,
.matchsize = sizeof(struct xt_udp),
.proto = IPPROTO_UDPLITE,
.me = THIS_MODULE,
},
};
static int __init xt_tcpudp_init(void)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment