diff --git a/Documentation/networking/dccp.txt b/Documentation/networking/dccp.txt index c45daabd3bfe..74563b38ffd9 100644 --- a/Documentation/networking/dccp.txt +++ b/Documentation/networking/dccp.txt @@ -1,7 +1,6 @@ DCCP protocol ============ -Last updated: 10 November 2005 Contents ======== @@ -42,8 +41,11 @@ Socket options DCCP_SOCKOPT_PACKET_SIZE is used for CCID3 to set default packet size for calculations. -DCCP_SOCKOPT_SERVICE sets the service. This is compulsory as per the -specification. If you don't set it you will get EPROTO. +DCCP_SOCKOPT_SERVICE sets the service. The specification mandates use of +service codes (RFC 4340, sec. 8.1.2); if this socket option is not set, +the socket will fall back to 0 (which means that no meaningful service code +is present). Connecting sockets set at most one service option; for +listening sockets, multiple service codes can be specified. Notes ===== diff --git a/MAINTAINERS b/MAINTAINERS index 16af51ba44a5..23348c0d37bc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2031,6 +2031,13 @@ L: netfilter@lists.netfilter.org L: netfilter-devel@lists.netfilter.org S: Supported +NETLABEL +P: Paul Moore +M: paul.moore@hp.com +W: http://netlabel.sf.net +L: netdev@vger.kernel.org +S: Supported + NETROM NETWORK LAYER P: Ralf Baechle M: ralf@linux-mips.org diff --git a/drivers/atm/he.c b/drivers/atm/he.c index 41e052fecd7f..f2511b42dba2 100644 --- a/drivers/atm/he.c +++ b/drivers/atm/he.c @@ -454,7 +454,7 @@ rate_to_atmf(unsigned rate) /* cps to atm forum format */ return (NONZERO | (exp << 9) | (rate & 0x1ff)); } -static void __init +static void __devinit he_init_rx_lbfp0(struct he_dev *he_dev) { unsigned i, lbm_offset, lbufd_index, lbuf_addr, lbuf_count; @@ -485,7 +485,7 @@ he_init_rx_lbfp0(struct he_dev *he_dev) he_writel(he_dev, he_dev->r0_numbuffs, RLBF0_C); } -static void __init +static void __devinit he_init_rx_lbfp1(struct he_dev *he_dev) { unsigned i, lbm_offset, lbufd_index, lbuf_addr, lbuf_count; @@ -516,7 +516,7 @@ he_init_rx_lbfp1(struct he_dev *he_dev) he_writel(he_dev, he_dev->r1_numbuffs, RLBF1_C); } -static void __init +static void __devinit he_init_tx_lbfp(struct he_dev *he_dev) { unsigned i, lbm_offset, lbufd_index, lbuf_addr, lbuf_count; @@ -546,7 +546,7 @@ he_init_tx_lbfp(struct he_dev *he_dev) he_writel(he_dev, lbufd_index - 1, TLBF_T); } -static int __init +static int __devinit he_init_tpdrq(struct he_dev *he_dev) { he_dev->tpdrq_base = pci_alloc_consistent(he_dev->pci_dev, @@ -568,7 +568,7 @@ he_init_tpdrq(struct he_dev *he_dev) return 0; } -static void __init +static void __devinit he_init_cs_block(struct he_dev *he_dev) { unsigned clock, rate, delta; @@ -664,7 +664,7 @@ he_init_cs_block(struct he_dev *he_dev) } -static int __init +static int __devinit he_init_cs_block_rcm(struct he_dev *he_dev) { unsigned (*rategrid)[16][16]; @@ -785,7 +785,7 @@ he_init_cs_block_rcm(struct he_dev *he_dev) return 0; } -static int __init +static int __devinit he_init_group(struct he_dev *he_dev, int group) { int i; @@ -955,7 +955,7 @@ he_init_group(struct he_dev *he_dev, int group) return 0; } -static int __init +static int __devinit he_init_irq(struct he_dev *he_dev) { int i; diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 2d7671c92c0b..d6f4ec467a4b 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -169,6 +169,12 @@ enum { DCCPO_MAX_CCID_SPECIFIC = 255, }; +/* DCCP CCIDS */ +enum { + DCCPC_CCID2 = 2, + DCCPC_CCID3 = 3, +}; + /* DCCP features */ enum { DCCPF_RESERVED = 0, @@ -320,7 +326,7 @@ static inline unsigned int dccp_hdr_len(const struct sk_buff *skb) /* initial values for each feature */ #define DCCPF_INITIAL_SEQUENCE_WINDOW 100 #define DCCPF_INITIAL_ACK_RATIO 2 -#define DCCPF_INITIAL_CCID 2 +#define DCCPF_INITIAL_CCID DCCPC_CCID2 #define DCCPF_INITIAL_SEND_ACK_VECTOR 1 /* FIXME: for now we're default to 1 but it should really be 0 */ #define DCCPF_INITIAL_SEND_NDP_COUNT 1 @@ -404,6 +410,7 @@ struct dccp_service_list { }; #define DCCP_SERVICE_INVALID_VALUE htonl((__u32)-1) +#define DCCP_SERVICE_CODE_IS_ABSENT 0 static inline int dccp_list_has_service(const struct dccp_service_list *sl, const __be32 service) @@ -484,11 +491,6 @@ static inline struct dccp_minisock *dccp_msk(const struct sock *sk) return (struct dccp_minisock *)&dccp_sk(sk)->dccps_minisock; } -static inline int dccp_service_not_initialized(const struct sock *sk) -{ - return dccp_sk(sk)->dccps_service == DCCP_SERVICE_INVALID_VALUE; -} - static inline const char *dccp_role(const struct sock *sk) { switch (dccp_sk(sk)->dccps_role) { diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild index 9a285cecf249..312bd2ffee33 100644 --- a/include/linux/netfilter/Kbuild +++ b/include/linux/netfilter/Kbuild @@ -10,6 +10,8 @@ header-y += xt_connmark.h header-y += xt_CONNMARK.h header-y += xt_conntrack.h header-y += xt_dccp.h +header-y += xt_dscp.h +header-y += xt_DSCP.h header-y += xt_esp.h header-y += xt_helper.h header-y += xt_length.h diff --git a/include/net/cipso_ipv4.h b/include/net/cipso_ipv4.h index 59406e0dc5b2..2d72496c2029 100644 --- a/include/net/cipso_ipv4.h +++ b/include/net/cipso_ipv4.h @@ -130,8 +130,9 @@ extern int cipso_v4_rbm_strictvalid; int cipso_v4_doi_add(struct cipso_v4_doi *doi_def); int cipso_v4_doi_remove(u32 doi, void (*callback) (struct rcu_head * head)); struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi); -struct sk_buff *cipso_v4_doi_dump_all(size_t headroom); -struct sk_buff *cipso_v4_doi_dump(u32 doi, size_t headroom); +int cipso_v4_doi_walk(u32 *skip_cnt, + int (*callback) (struct cipso_v4_doi *doi_def, void *arg), + void *cb_arg); int cipso_v4_doi_domhsh_add(struct cipso_v4_doi *doi_def, const char *domain); int cipso_v4_doi_domhsh_remove(struct cipso_v4_doi *doi_def, const char *domain); @@ -152,14 +153,11 @@ static inline struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi) return NULL; } -static inline struct sk_buff *cipso_v4_doi_dump_all(size_t headroom) +static inline int cipso_v4_doi_walk(u32 *skip_cnt, + int (*callback) (struct cipso_v4_doi *doi_def, void *arg), + void *cb_arg) { - return NULL; -} - -static inline struct sk_buff *cipso_v4_doi_dump(u32 doi, size_t headroom) -{ - return NULL; + return 0; } static inline int cipso_v4_doi_domhsh_add(struct cipso_v4_doi *doi_def, @@ -205,6 +203,7 @@ void cipso_v4_error(struct sk_buff *skb, int error, u32 gateway); int cipso_v4_socket_setattr(const struct socket *sock, const struct cipso_v4_doi *doi_def, const struct netlbl_lsm_secattr *secattr); +int cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr); int cipso_v4_socket_getattr(const struct socket *sock, struct netlbl_lsm_secattr *secattr); int cipso_v4_skbuff_getattr(const struct sk_buff *skb, @@ -225,6 +224,12 @@ static inline int cipso_v4_socket_setattr(const struct socket *sock, return -ENOSYS; } +static inline int cipso_v4_sock_getattr(struct sock *sk, + struct netlbl_lsm_secattr *secattr) +{ + return -ENOSYS; +} + static inline int cipso_v4_socket_getattr(const struct socket *sock, struct netlbl_lsm_secattr *secattr) { diff --git a/include/net/netlabel.h b/include/net/netlabel.h index dd5780b36919..6692430063fd 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -57,9 +57,8 @@ * The payload is dependent on the subsystem specified in the * 'nlmsghdr->nlmsg_type' and should be defined below, supporting functions * should be defined in the corresponding net/netlabel/netlabel_.h|c - * file. All of the fields in the NetLabel payload are NETLINK attributes, the - * length of each field is the length of the NETLINK attribute payload, see - * include/net/netlink.h for more information on NETLINK attributes. + * file. All of the fields in the NetLabel payload are NETLINK attributes, see + * the include/net/netlink.h file for more information on NETLINK attributes. * */ @@ -82,50 +81,6 @@ #define NETLBL_NLTYPE_UNLABELED 5 #define NETLBL_NLTYPE_UNLABELED_NAME "NLBL_UNLBL" -/* NetLabel return codes */ -#define NETLBL_E_OK 0 - -/* - * Helper functions - */ - -#define NETLBL_LEN_U8 nla_total_size(sizeof(u8)) -#define NETLBL_LEN_U16 nla_total_size(sizeof(u16)) -#define NETLBL_LEN_U32 nla_total_size(sizeof(u32)) - -/** - * netlbl_netlink_alloc_skb - Allocate a NETLINK message buffer - * @head: the amount of headroom in bytes - * @body: the desired size (minus headroom) in bytes - * @gfp_flags: the alloc flags to pass to alloc_skb() - * - * Description: - * Allocate a NETLINK message buffer based on the sizes given in @head and - * @body. If @head is greater than zero skb_reserve() is called to reserve - * @head bytes at the start of the buffer. Returns a valid sk_buff pointer on - * success, NULL on failure. - * - */ -static inline struct sk_buff *netlbl_netlink_alloc_skb(size_t head, - size_t body, - gfp_t gfp_flags) -{ - struct sk_buff *skb; - - skb = alloc_skb(NLMSG_ALIGN(head + body), gfp_flags); - if (skb == NULL) - return NULL; - if (head > 0) { - skb_reserve(skb, head); - if (skb_tailroom(skb) < body) { - kfree_skb(skb); - return NULL; - } - } - - return skb; -} - /* * NetLabel - Kernel API for accessing the network packet label mappings. * @@ -238,6 +193,8 @@ static inline void netlbl_secattr_free(struct netlbl_lsm_secattr *secattr, #ifdef CONFIG_NETLABEL int netlbl_socket_setattr(const struct socket *sock, const struct netlbl_lsm_secattr *secattr); +int netlbl_sock_getattr(struct sock *sk, + struct netlbl_lsm_secattr *secattr); int netlbl_socket_getattr(const struct socket *sock, struct netlbl_lsm_secattr *secattr); int netlbl_skbuff_getattr(const struct sk_buff *skb, @@ -250,6 +207,12 @@ static inline int netlbl_socket_setattr(const struct socket *sock, return -ENOSYS; } +static inline int netlbl_sock_getattr(struct sock *sk, + struct netlbl_lsm_secattr *secattr) +{ + return -ENOSYS; +} + static inline int netlbl_socket_getattr(const struct socket *sock, struct netlbl_lsm_secattr *secattr) { diff --git a/include/net/netlink.h b/include/net/netlink.h index 11dc2e7f679a..4ab68a7a636a 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -146,11 +146,13 @@ * nla_ok(nla, remaining) does nla fit into remaining bytes? * nla_next(nla, remaining) get next netlink attribute * nla_validate() validate a stream of attributes + * nla_validate_nested() validate a stream of nested attributes * nla_find() find attribute in stream of attributes * nla_find_nested() find attribute in nested attributes * nla_parse() parse and validate stream of attrs * nla_parse_nested() parse nested attribuets * nla_for_each_attr() loop over all attributes + * nla_for_each_nested() loop over the nested attributes *========================================================================= */ @@ -949,6 +951,24 @@ static inline int nla_nest_cancel(struct sk_buff *skb, struct nlattr *start) return nlmsg_trim(skb, start); } +/** + * nla_validate_nested - Validate a stream of nested attributes + * @start: container attribute + * @maxtype: maximum attribute type to be expected + * @policy: validation policy + * + * Validates all attributes in the nested attribute stream against the + * specified policy. Attributes with a type exceeding maxtype will be + * ignored. See documenation of struct nla_policy for more details. + * + * Returns 0 on success or a negative error code. + */ +static inline int nla_validate_nested(struct nlattr *start, int maxtype, + struct nla_policy *policy) +{ + return nla_validate(nla_data(start), nla_len(start), maxtype, policy); +} + /** * nla_for_each_attr - iterate over a stream of attributes * @pos: loop counter, set to current attribute diff --git a/net/dccp/Kconfig b/net/dccp/Kconfig index 859e3359fcda..e2a095d0fd80 100644 --- a/net/dccp/Kconfig +++ b/net/dccp/Kconfig @@ -40,6 +40,22 @@ config IP_DCCP_DEBUG Just say N. +config NET_DCCPPROBE + tristate "DCCP connection probing" + depends on PROC_FS && KPROBES + ---help--- + This module allows for capturing the changes to DCCP connection + state in response to incoming packets. It is used for debugging + DCCP congestion avoidance modules. If you don't understand + what was just said, you don't need it: say N. + + Documentation on how to use the packet generator can be found + at http://linux-net.osdl.org/index.php/DccpProbe + + To compile this code as a module, choose M here: the + module will be called dccp_probe. + + endmenu endmenu diff --git a/net/dccp/Makefile b/net/dccp/Makefile index 7696e219b05d..17ed99c46617 100644 --- a/net/dccp/Makefile +++ b/net/dccp/Makefile @@ -11,9 +11,11 @@ dccp_ipv4-y := ipv4.o dccp-$(CONFIG_IP_DCCP_ACKVEC) += ackvec.o obj-$(CONFIG_INET_DCCP_DIAG) += dccp_diag.o +obj-$(CONFIG_NET_DCCPPROBE) += dccp_probe.o dccp-$(CONFIG_SYSCTL) += sysctl.o dccp_diag-y := diag.o +dccp_probe-y := probe.o obj-y += ccids/ diff --git a/net/dccp/ccids/ccid2.c b/net/dccp/ccids/ccid2.c index 457dd3db7f41..2efb505aeb35 100644 --- a/net/dccp/ccids/ccid2.c +++ b/net/dccp/ccids/ccid2.c @@ -808,7 +808,7 @@ static void ccid2_hc_rx_packet_recv(struct sock *sk, struct sk_buff *skb) } static struct ccid_operations ccid2 = { - .ccid_id = 2, + .ccid_id = DCCPC_CCID2, .ccid_name = "ccid2", .ccid_owner = THIS_MODULE, .ccid_hc_tx_obj_size = sizeof(struct ccid2_hc_tx_sock), diff --git a/net/dccp/ccids/ccid3.c b/net/dccp/ccids/ccid3.c index 195aa9566228..67d2dc0e7c67 100644 --- a/net/dccp/ccids/ccid3.c +++ b/net/dccp/ccids/ccid3.c @@ -1240,7 +1240,7 @@ static int ccid3_hc_tx_getsockopt(struct sock *sk, const int optname, int len, } static struct ccid_operations ccid3 = { - .ccid_id = 3, + .ccid_id = DCCPC_CCID3, .ccid_name = "ccid3", .ccid_owner = THIS_MODULE, .ccid_hc_tx_obj_size = sizeof(struct ccid3_hc_tx_sock), diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 9a1a76a7dc41..66be29b6f508 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -56,9 +56,6 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) dp->dccps_role = DCCP_ROLE_CLIENT; - if (dccp_service_not_initialized(sk)) - return -EPROTO; - if (addr_len < sizeof(struct sockaddr_in)) return -EINVAL; diff --git a/net/dccp/probe.c b/net/dccp/probe.c new file mode 100644 index 000000000000..146496fce2e2 --- /dev/null +++ b/net/dccp/probe.c @@ -0,0 +1,198 @@ +/* + * dccp_probe - Observe the DCCP flow with kprobes. + * + * The idea for this came from Werner Almesberger's umlsim + * Copyright (C) 2004, Stephen Hemminger + * + * Modified for DCCP from Stephen Hemminger's code + * Copyright (C) 2006, Ian McDonald + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dccp.h" +#include "ccid.h" +#include "ccids/ccid3.h" + +static int port; + +static int bufsize = 64 * 1024; + +static const char procname[] = "dccpprobe"; + +struct { + struct kfifo *fifo; + spinlock_t lock; + wait_queue_head_t wait; + struct timeval tstart; +} dccpw; + +static void printl(const char *fmt, ...) +{ + va_list args; + int len; + struct timeval now; + char tbuf[256]; + + va_start(args, fmt); + do_gettimeofday(&now); + + now.tv_sec -= dccpw.tstart.tv_sec; + now.tv_usec -= dccpw.tstart.tv_usec; + if (now.tv_usec < 0) { + --now.tv_sec; + now.tv_usec += 1000000; + } + + len = sprintf(tbuf, "%lu.%06lu ", + (unsigned long) now.tv_sec, + (unsigned long) now.tv_usec); + len += vscnprintf(tbuf+len, sizeof(tbuf)-len, fmt, args); + va_end(args); + + kfifo_put(dccpw.fifo, tbuf, len); + wake_up(&dccpw.wait); +} + +static int jdccp_sendmsg(struct kiocb *iocb, struct sock *sk, + struct msghdr *msg, size_t size) +{ + const struct dccp_minisock *dmsk = dccp_msk(sk); + const struct inet_sock *inet = inet_sk(sk); + const struct ccid3_hc_tx_sock *hctx; + + if (dmsk->dccpms_tx_ccid == DCCPC_CCID3) + hctx = ccid3_hc_tx_sk(sk); + else + hctx = NULL; + + if (port == 0 || ntohs(inet->dport) == port || + ntohs(inet->sport) == port) { + if (hctx) + printl("%d.%d.%d.%d:%u %d.%d.%d.%d:%u %d %d %d %d %d\n", + NIPQUAD(inet->saddr), ntohs(inet->sport), + NIPQUAD(inet->daddr), ntohs(inet->dport), size, + hctx->ccid3hctx_s, hctx->ccid3hctx_rtt, + hctx->ccid3hctx_p, hctx->ccid3hctx_t_ipi); + else + printl("%d.%d.%d.%d:%u %d.%d.%d.%d:%u %d\n", + NIPQUAD(inet->saddr), ntohs(inet->sport), + NIPQUAD(inet->daddr), ntohs(inet->dport), size); + } + + jprobe_return(); + return 0; +} + +static struct jprobe dccp_send_probe = { + .kp = { .addr = (kprobe_opcode_t *)&dccp_sendmsg, }, + .entry = (kprobe_opcode_t *)&jdccp_sendmsg, +}; + +static int dccpprobe_open(struct inode *inode, struct file *file) +{ + kfifo_reset(dccpw.fifo); + do_gettimeofday(&dccpw.tstart); + return 0; +} + +static ssize_t dccpprobe_read(struct file *file, char __user *buf, + size_t len, loff_t *ppos) +{ + int error = 0, cnt = 0; + unsigned char *tbuf; + + if (!buf || len < 0) + return -EINVAL; + + if (len == 0) + return 0; + + tbuf = vmalloc(len); + if (!tbuf) + return -ENOMEM; + + error = wait_event_interruptible(dccpw.wait, + __kfifo_len(dccpw.fifo) != 0); + if (error) + goto out_free; + + cnt = kfifo_get(dccpw.fifo, tbuf, len); + error = copy_to_user(buf, tbuf, cnt); + +out_free: + vfree(tbuf); + + return error ? error : cnt; +} + +static struct file_operations dccpprobe_fops = { + .owner = THIS_MODULE, + .open = dccpprobe_open, + .read = dccpprobe_read, +}; + +static __init int dccpprobe_init(void) +{ + int ret = -ENOMEM; + + init_waitqueue_head(&dccpw.wait); + spin_lock_init(&dccpw.lock); + dccpw.fifo = kfifo_alloc(bufsize, GFP_KERNEL, &dccpw.lock); + + if (!proc_net_fops_create(procname, S_IRUSR, &dccpprobe_fops)) + goto err0; + + ret = register_jprobe(&dccp_send_probe); + if (ret) + goto err1; + + pr_info("DCCP watch registered (port=%d)\n", port); + return 0; +err1: + proc_net_remove(procname); +err0: + kfifo_free(dccpw.fifo); + return ret; +} +module_init(dccpprobe_init); + +static __exit void dccpprobe_exit(void) +{ + kfifo_free(dccpw.fifo); + proc_net_remove(procname); + unregister_jprobe(&dccp_send_probe); + +} +module_exit(dccpprobe_exit); + +MODULE_PARM_DESC(port, "Port to match (0=all)"); +module_param(port, int, 0); + +MODULE_PARM_DESC(bufsize, "Log buffer size (default 64k)"); +module_param(bufsize, int, 0); + +MODULE_AUTHOR("Ian McDonald "); +MODULE_DESCRIPTION("DCCP snooper"); +MODULE_LICENSE("GPL"); diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 962df0ea31aa..72cbdcfc2c65 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -217,7 +217,7 @@ int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized) icsk->icsk_sync_mss = dccp_sync_mss; dp->dccps_mss_cache = 536; dp->dccps_role = DCCP_ROLE_UNDEFINED; - dp->dccps_service = DCCP_SERVICE_INVALID_VALUE; + dp->dccps_service = DCCP_SERVICE_CODE_IS_ABSENT; dp->dccps_l_ack_ratio = dp->dccps_r_ack_ratio = 1; return 0; @@ -267,12 +267,6 @@ static inline int dccp_listen_start(struct sock *sk) struct dccp_sock *dp = dccp_sk(sk); dp->dccps_role = DCCP_ROLE_LISTEN; - /* - * Apps need to use setsockopt(DCCP_SOCKOPT_SERVICE) - * before calling listen() - */ - if (dccp_service_not_initialized(sk)) - return -EPROTO; return inet_csk_listen_start(sk, TCP_SYNQ_HSIZE); } @@ -540,9 +534,6 @@ static int dccp_getsockopt_service(struct sock *sk, int len, int err = -ENOENT, slen = 0, total_len = sizeof(u32); lock_sock(sk); - if (dccp_service_not_initialized(sk)) - goto out; - if ((sl = dp->dccps_service_list) != NULL) { slen = sl->dccpsl_nr * sizeof(u32); total_len += slen; diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index 1650b64415aa..30af4a4dfcc8 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -448,24 +448,22 @@ config INET_TCP_DIAG depends on INET_DIAG def_tristate INET_DIAG -config TCP_CONG_ADVANCED +menuconfig TCP_CONG_ADVANCED bool "TCP: advanced congestion control" ---help--- Support for selection of various TCP congestion control modules. Nearly all users can safely say no here, and a safe default - selection will be made (BIC-TCP with new Reno as a fallback). + selection will be made (CUBIC with new Reno as a fallback). If unsure, say N. -# TCP Reno is builtin (required as fallback) -menu "TCP congestion control" - depends on TCP_CONG_ADVANCED +if TCP_CONG_ADVANCED config TCP_CONG_BIC tristate "Binary Increase Congestion (BIC) control" - default y + default m ---help--- BIC-TCP is a sender-side only change that ensures a linear RTT fairness under large windows while offering both scalability and @@ -479,7 +477,7 @@ config TCP_CONG_BIC config TCP_CONG_CUBIC tristate "CUBIC TCP" - default m + default y ---help--- This is version 2.0 of BIC-TCP which uses a cubic growth function among other techniques. @@ -574,12 +572,49 @@ config TCP_CONG_VENO loss packets. See http://www.ntu.edu.sg/home5/ZHOU0022/papers/CPFu03a.pdf -endmenu +choice + prompt "Default TCP congestion control" + default DEFAULT_CUBIC + help + Select the TCP congestion control that will be used by default + for all connections. -config TCP_CONG_BIC + config DEFAULT_BIC + bool "Bic" if TCP_CONG_BIC=y + + config DEFAULT_CUBIC + bool "Cubic" if TCP_CONG_CUBIC=y + + config DEFAULT_HTCP + bool "Htcp" if TCP_CONG_HTCP=y + + config DEFAULT_VEGAS + bool "Vegas" if TCP_CONG_VEGAS=y + + config DEFAULT_WESTWOOD + bool "Westwood" if TCP_CONG_WESTWOOD=y + + config DEFAULT_RENO + bool "Reno" + +endchoice + +endif + +config TCP_CONG_CUBIC tristate depends on !TCP_CONG_ADVANCED default y +config DEFAULT_TCP_CONG + string + default "bic" if DEFAULT_BIC + default "cubic" if DEFAULT_CUBIC + default "htcp" if DEFAULT_HTCP + default "vegas" if DEFAULT_VEGAS + default "westwood" if DEFAULT_WESTWOOD + default "reno" if DEFAULT_RENO + default "cubic" + source "net/ipv4/ipvs/Kconfig" diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c index 80a2a0911b49..e6ce0b3ba62a 100644 --- a/net/ipv4/cipso_ipv4.c +++ b/net/ipv4/cipso_ipv4.c @@ -259,7 +259,7 @@ void cipso_v4_cache_invalidate(void) u32 iter; for (iter = 0; iter < CIPSO_V4_CACHE_BUCKETS; iter++) { - spin_lock(&cipso_v4_cache[iter].lock); + spin_lock_bh(&cipso_v4_cache[iter].lock); list_for_each_entry_safe(entry, tmp_entry, &cipso_v4_cache[iter].list, list) { @@ -267,7 +267,7 @@ void cipso_v4_cache_invalidate(void) cipso_v4_cache_entry_free(entry); } cipso_v4_cache[iter].size = 0; - spin_unlock(&cipso_v4_cache[iter].lock); + spin_unlock_bh(&cipso_v4_cache[iter].lock); } return; @@ -309,7 +309,7 @@ static int cipso_v4_cache_check(const unsigned char *key, hash = cipso_v4_map_cache_hash(key, key_len); bkt = hash & (CIPSO_V4_CACHE_BUCKETBITS - 1); - spin_lock(&cipso_v4_cache[bkt].lock); + spin_lock_bh(&cipso_v4_cache[bkt].lock); list_for_each_entry(entry, &cipso_v4_cache[bkt].list, list) { if (entry->hash == hash && entry->key_len == key_len && @@ -318,7 +318,7 @@ static int cipso_v4_cache_check(const unsigned char *key, secattr->cache.free = entry->lsm_data.free; secattr->cache.data = entry->lsm_data.data; if (prev_entry == NULL) { - spin_unlock(&cipso_v4_cache[bkt].lock); + spin_unlock_bh(&cipso_v4_cache[bkt].lock); return 0; } @@ -333,12 +333,12 @@ static int cipso_v4_cache_check(const unsigned char *key, &prev_entry->list); } - spin_unlock(&cipso_v4_cache[bkt].lock); + spin_unlock_bh(&cipso_v4_cache[bkt].lock); return 0; } prev_entry = entry; } - spin_unlock(&cipso_v4_cache[bkt].lock); + spin_unlock_bh(&cipso_v4_cache[bkt].lock); return -ENOENT; } @@ -387,7 +387,7 @@ int cipso_v4_cache_add(const struct sk_buff *skb, entry->lsm_data.data = secattr->cache.data; bkt = entry->hash & (CIPSO_V4_CACHE_BUCKETBITS - 1); - spin_lock(&cipso_v4_cache[bkt].lock); + spin_lock_bh(&cipso_v4_cache[bkt].lock); if (cipso_v4_cache[bkt].size < cipso_v4_cache_bucketsize) { list_add(&entry->list, &cipso_v4_cache[bkt].list); cipso_v4_cache[bkt].size += 1; @@ -398,7 +398,7 @@ int cipso_v4_cache_add(const struct sk_buff *skb, list_add(&entry->list, &cipso_v4_cache[bkt].list); cipso_v4_cache_entry_free(old_entry); } - spin_unlock(&cipso_v4_cache[bkt].lock); + spin_unlock_bh(&cipso_v4_cache[bkt].lock); return 0; @@ -530,197 +530,42 @@ struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi) } /** - * cipso_v4_doi_dump_all - Dump all the CIPSO DOI definitions into a sk_buff - * @headroom: the amount of headroom to allocate for the sk_buff + * cipso_v4_doi_walk - Iterate through the DOI definitions + * @skip_cnt: skip past this number of DOI definitions, updated + * @callback: callback for each DOI definition + * @cb_arg: argument for the callback function * * Description: - * Dump a list of all the configured DOI values into a sk_buff. The returned - * sk_buff has room at the front of the sk_buff for @headroom bytes. See - * net/netlabel/netlabel_cipso_v4.h for the LISTALL message format. This - * function may fail if another process is changing the DOI list at the same - * time. Returns a pointer to a sk_buff on success, NULL on error. + * Iterate over the DOI definition list, skipping the first @skip_cnt entries. + * For each entry call @callback, if @callback returns a negative value stop + * 'walking' through the list and return. Updates the value in @skip_cnt upon + * return. Returns zero on success, negative values on failure. * */ -struct sk_buff *cipso_v4_doi_dump_all(size_t headroom) +int cipso_v4_doi_walk(u32 *skip_cnt, + int (*callback) (struct cipso_v4_doi *doi_def, void *arg), + void *cb_arg) { - struct sk_buff *skb = NULL; - struct cipso_v4_doi *iter; + int ret_val = -ENOENT; u32 doi_cnt = 0; - ssize_t buf_len; - - buf_len = NETLBL_LEN_U32; - rcu_read_lock(); - list_for_each_entry_rcu(iter, &cipso_v4_doi_list, list) - if (iter->valid) { - doi_cnt += 1; - buf_len += 2 * NETLBL_LEN_U32; - } - - skb = netlbl_netlink_alloc_skb(headroom, buf_len, GFP_ATOMIC); - if (skb == NULL) - goto doi_dump_all_failure; - - if (nla_put_u32(skb, NLA_U32, doi_cnt) != 0) - goto doi_dump_all_failure; - buf_len -= NETLBL_LEN_U32; - list_for_each_entry_rcu(iter, &cipso_v4_doi_list, list) - if (iter->valid) { - if (buf_len < 2 * NETLBL_LEN_U32) - goto doi_dump_all_failure; - if (nla_put_u32(skb, NLA_U32, iter->doi) != 0) - goto doi_dump_all_failure; - if (nla_put_u32(skb, NLA_U32, iter->type) != 0) - goto doi_dump_all_failure; - buf_len -= 2 * NETLBL_LEN_U32; - } - rcu_read_unlock(); - - return skb; - -doi_dump_all_failure: - rcu_read_unlock(); - kfree(skb); - return NULL; -} - -/** - * cipso_v4_doi_dump - Dump a CIPSO DOI definition into a sk_buff - * @doi: the DOI value - * @headroom: the amount of headroom to allocate for the sk_buff - * - * Description: - * Lookup the DOI definition matching @doi and dump it's contents into a - * sk_buff. The returned sk_buff has room at the front of the sk_buff for - * @headroom bytes. See net/netlabel/netlabel_cipso_v4.h for the LIST message - * format. This function may fail if another process is changing the DOI list - * at the same time. Returns a pointer to a sk_buff on success, NULL on error. - * - */ -struct sk_buff *cipso_v4_doi_dump(u32 doi, size_t headroom) -{ - struct sk_buff *skb = NULL; - struct cipso_v4_doi *iter; - u32 tag_cnt = 0; - u32 lvl_cnt = 0; - u32 cat_cnt = 0; - ssize_t buf_len; - ssize_t tmp; + struct cipso_v4_doi *iter_doi; rcu_read_lock(); - iter = cipso_v4_doi_getdef(doi); - if (iter == NULL) - goto doi_dump_failure; - buf_len = NETLBL_LEN_U32; - switch (iter->type) { - case CIPSO_V4_MAP_PASS: - buf_len += NETLBL_LEN_U32; - while(tag_cnt < CIPSO_V4_TAG_MAXCNT && - iter->tags[tag_cnt] != CIPSO_V4_TAG_INVALID) { - tag_cnt += 1; - buf_len += NETLBL_LEN_U8; - } - break; - case CIPSO_V4_MAP_STD: - buf_len += 3 * NETLBL_LEN_U32; - while (tag_cnt < CIPSO_V4_TAG_MAXCNT && - iter->tags[tag_cnt] != CIPSO_V4_TAG_INVALID) { - tag_cnt += 1; - buf_len += NETLBL_LEN_U8; - } - for (tmp = 0; tmp < iter->map.std->lvl.local_size; tmp++) - if (iter->map.std->lvl.local[tmp] != - CIPSO_V4_INV_LVL) { - lvl_cnt += 1; - buf_len += NETLBL_LEN_U32 + NETLBL_LEN_U8; + list_for_each_entry_rcu(iter_doi, &cipso_v4_doi_list, list) + if (iter_doi->valid) { + if (doi_cnt++ < *skip_cnt) + continue; + ret_val = callback(iter_doi, cb_arg); + if (ret_val < 0) { + doi_cnt--; + goto doi_walk_return; } - for (tmp = 0; tmp < iter->map.std->cat.local_size; tmp++) - if (iter->map.std->cat.local[tmp] != - CIPSO_V4_INV_CAT) { - cat_cnt += 1; - buf_len += NETLBL_LEN_U32 + NETLBL_LEN_U16; - } - break; - } + } - skb = netlbl_netlink_alloc_skb(headroom, buf_len, GFP_ATOMIC); - if (skb == NULL) - goto doi_dump_failure; - - if (nla_put_u32(skb, NLA_U32, iter->type) != 0) - goto doi_dump_failure; - buf_len -= NETLBL_LEN_U32; - if (iter != cipso_v4_doi_getdef(doi)) - goto doi_dump_failure; - switch (iter->type) { - case CIPSO_V4_MAP_PASS: - if (nla_put_u32(skb, NLA_U32, tag_cnt) != 0) - goto doi_dump_failure; - buf_len -= NETLBL_LEN_U32; - for (tmp = 0; - tmp < CIPSO_V4_TAG_MAXCNT && - iter->tags[tmp] != CIPSO_V4_TAG_INVALID; - tmp++) { - if (buf_len < NETLBL_LEN_U8) - goto doi_dump_failure; - if (nla_put_u8(skb, NLA_U8, iter->tags[tmp]) != 0) - goto doi_dump_failure; - buf_len -= NETLBL_LEN_U8; - } - break; - case CIPSO_V4_MAP_STD: - if (nla_put_u32(skb, NLA_U32, tag_cnt) != 0) - goto doi_dump_failure; - if (nla_put_u32(skb, NLA_U32, lvl_cnt) != 0) - goto doi_dump_failure; - if (nla_put_u32(skb, NLA_U32, cat_cnt) != 0) - goto doi_dump_failure; - buf_len -= 3 * NETLBL_LEN_U32; - for (tmp = 0; - tmp < CIPSO_V4_TAG_MAXCNT && - iter->tags[tmp] != CIPSO_V4_TAG_INVALID; - tmp++) { - if (buf_len < NETLBL_LEN_U8) - goto doi_dump_failure; - if (nla_put_u8(skb, NLA_U8, iter->tags[tmp]) != 0) - goto doi_dump_failure; - buf_len -= NETLBL_LEN_U8; - } - for (tmp = 0; tmp < iter->map.std->lvl.local_size; tmp++) - if (iter->map.std->lvl.local[tmp] != - CIPSO_V4_INV_LVL) { - if (buf_len < NETLBL_LEN_U32 + NETLBL_LEN_U8) - goto doi_dump_failure; - if (nla_put_u32(skb, NLA_U32, tmp) != 0) - goto doi_dump_failure; - if (nla_put_u8(skb, - NLA_U8, - iter->map.std->lvl.local[tmp]) != 0) - goto doi_dump_failure; - buf_len -= NETLBL_LEN_U32 + NETLBL_LEN_U8; - } - for (tmp = 0; tmp < iter->map.std->cat.local_size; tmp++) - if (iter->map.std->cat.local[tmp] != - CIPSO_V4_INV_CAT) { - if (buf_len < NETLBL_LEN_U32 + NETLBL_LEN_U16) - goto doi_dump_failure; - if (nla_put_u32(skb, NLA_U32, tmp) != 0) - goto doi_dump_failure; - if (nla_put_u16(skb, - NLA_U16, - iter->map.std->cat.local[tmp]) != 0) - goto doi_dump_failure; - buf_len -= NETLBL_LEN_U32 + NETLBL_LEN_U16; - } - break; - } +doi_walk_return: rcu_read_unlock(); - - return skb; - -doi_dump_failure: - rcu_read_unlock(); - kfree(skb); - return NULL; + *skip_cnt = doi_cnt; + return ret_val; } /** @@ -1485,6 +1330,54 @@ socket_setattr_failure: return ret_val; } +/** + * cipso_v4_sock_getattr - Get the security attributes from a sock + * @sk: the sock + * @secattr: the security attributes + * + * Description: + * Query @sk to see if there is a CIPSO option attached to the sock and if + * there is return the CIPSO security attributes in @secattr. This function + * requires that @sk be locked, or privately held, but it does not do any + * locking itself. Returns zero on success and negative values on failure. + * + */ +int cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr) +{ + int ret_val = -ENOMSG; + struct inet_sock *sk_inet; + unsigned char *cipso_ptr; + u32 doi; + struct cipso_v4_doi *doi_def; + + sk_inet = inet_sk(sk); + if (sk_inet->opt == NULL || sk_inet->opt->cipso == 0) + return -ENOMSG; + cipso_ptr = sk_inet->opt->__data + sk_inet->opt->cipso - + sizeof(struct iphdr); + ret_val = cipso_v4_cache_check(cipso_ptr, cipso_ptr[1], secattr); + if (ret_val == 0) + return ret_val; + + doi = ntohl(*(u32 *)&cipso_ptr[2]); + rcu_read_lock(); + doi_def = cipso_v4_doi_getdef(doi); + if (doi_def == NULL) { + rcu_read_unlock(); + return -ENOMSG; + } + switch (cipso_ptr[6]) { + case CIPSO_V4_TAG_RBITMAP: + ret_val = cipso_v4_parsetag_rbm(doi_def, + &cipso_ptr[6], + secattr); + break; + } + rcu_read_unlock(); + + return ret_val; +} + /** * cipso_v4_socket_getattr - Get the security attributes from a socket * @sock: the socket @@ -1499,42 +1392,12 @@ socket_setattr_failure: int cipso_v4_socket_getattr(const struct socket *sock, struct netlbl_lsm_secattr *secattr) { - int ret_val = -ENOMSG; - struct sock *sk; - struct inet_sock *sk_inet; - unsigned char *cipso_ptr; - u32 doi; - struct cipso_v4_doi *doi_def; + int ret_val; - sk = sock->sk; - lock_sock(sk); - sk_inet = inet_sk(sk); - if (sk_inet->opt == NULL || sk_inet->opt->cipso == 0) - goto socket_getattr_return; - cipso_ptr = sk_inet->opt->__data + sk_inet->opt->cipso - - sizeof(struct iphdr); - ret_val = cipso_v4_cache_check(cipso_ptr, cipso_ptr[1], secattr); - if (ret_val == 0) - goto socket_getattr_return; + lock_sock(sock->sk); + ret_val = cipso_v4_sock_getattr(sock->sk, secattr); + release_sock(sock->sk); - doi = ntohl(*(u32 *)&cipso_ptr[2]); - rcu_read_lock(); - doi_def = cipso_v4_doi_getdef(doi); - if (doi_def == NULL) { - rcu_read_unlock(); - goto socket_getattr_return; - } - switch (cipso_ptr[6]) { - case CIPSO_V4_TAG_RBITMAP: - ret_val = cipso_v4_parsetag_rbm(doi_def, - &cipso_ptr[6], - secattr); - break; - } - rcu_read_unlock(); - -socket_getattr_return: - release_sock(sk); return ret_val; } diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 19b2071ff319..e82a5be894b5 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -129,6 +129,12 @@ static int sysctl_tcp_congestion_control(ctl_table *table, int __user *name, return ret; } +static int __init tcp_congestion_default(void) +{ + return tcp_set_default_congestion_control(CONFIG_DEFAULT_TCP_CONG); +} + +late_initcall(tcp_congestion_default); ctl_table ipv4_table[] = { { diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index 7ff2e4273a7c..af0aca1e6be6 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -48,7 +48,7 @@ int tcp_register_congestion_control(struct tcp_congestion_ops *ca) printk(KERN_NOTICE "TCP %s already registered\n", ca->name); ret = -EEXIST; } else { - list_add_rcu(&ca->list, &tcp_cong_list); + list_add_tail_rcu(&ca->list, &tcp_cong_list); printk(KERN_INFO "TCP %s registered\n", ca->name); } spin_unlock(&tcp_cong_list_lock); diff --git a/net/netlabel/Kconfig b/net/netlabel/Kconfig index fe23cb7f1e87..9f7121ae13e9 100644 --- a/net/netlabel/Kconfig +++ b/net/netlabel/Kconfig @@ -9,6 +9,9 @@ config NETLABEL ---help--- NetLabel provides support for explicit network packet labeling protocols such as CIPSO and RIPSO. For more information see - Documentation/netlabel. + Documentation/netlabel as well as the NetLabel SourceForge project + for configuration tools and additional documentation. + + * http://netlabel.sf.net If you are unsure, say N. diff --git a/net/netlabel/netlabel_cipso_v4.c b/net/netlabel/netlabel_cipso_v4.c index a4f40adc447b..4125a55f469f 100644 --- a/net/netlabel/netlabel_cipso_v4.c +++ b/net/netlabel/netlabel_cipso_v4.c @@ -41,15 +41,37 @@ #include "netlabel_user.h" #include "netlabel_cipso_v4.h" +/* Argument struct for cipso_v4_doi_walk() */ +struct netlbl_cipsov4_doiwalk_arg { + struct netlink_callback *nl_cb; + struct sk_buff *skb; + u32 seq; +}; + /* NetLabel Generic NETLINK CIPSOv4 family */ static struct genl_family netlbl_cipsov4_gnl_family = { .id = GENL_ID_GENERATE, .hdrsize = 0, .name = NETLBL_NLTYPE_CIPSOV4_NAME, .version = NETLBL_PROTO_VERSION, - .maxattr = 0, + .maxattr = NLBL_CIPSOV4_A_MAX, }; +/* NetLabel Netlink attribute policy */ +static struct nla_policy netlbl_cipsov4_genl_policy[NLBL_CIPSOV4_A_MAX + 1] = { + [NLBL_CIPSOV4_A_DOI] = { .type = NLA_U32 }, + [NLBL_CIPSOV4_A_MTYPE] = { .type = NLA_U32 }, + [NLBL_CIPSOV4_A_TAG] = { .type = NLA_U8 }, + [NLBL_CIPSOV4_A_TAGLST] = { .type = NLA_NESTED }, + [NLBL_CIPSOV4_A_MLSLVLLOC] = { .type = NLA_U32 }, + [NLBL_CIPSOV4_A_MLSLVLREM] = { .type = NLA_U32 }, + [NLBL_CIPSOV4_A_MLSLVL] = { .type = NLA_NESTED }, + [NLBL_CIPSOV4_A_MLSLVLLST] = { .type = NLA_NESTED }, + [NLBL_CIPSOV4_A_MLSCATLOC] = { .type = NLA_U32 }, + [NLBL_CIPSOV4_A_MLSCATREM] = { .type = NLA_U32 }, + [NLBL_CIPSOV4_A_MLSCAT] = { .type = NLA_NESTED }, + [NLBL_CIPSOV4_A_MLSCATLST] = { .type = NLA_NESTED }, +}; /* * Helper Functions @@ -81,6 +103,41 @@ static void netlbl_cipsov4_doi_free(struct rcu_head *entry) kfree(ptr); } +/** + * netlbl_cipsov4_add_common - Parse the common sections of a ADD message + * @info: the Generic NETLINK info block + * @doi_def: the CIPSO V4 DOI definition + * + * Description: + * Parse the common sections of a ADD message and fill in the related values + * in @doi_def. Returns zero on success, negative values on failure. + * + */ +static int netlbl_cipsov4_add_common(struct genl_info *info, + struct cipso_v4_doi *doi_def) +{ + struct nlattr *nla; + int nla_rem; + u32 iter = 0; + + doi_def->doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]); + + if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_TAGLST], + NLBL_CIPSOV4_A_MAX, + netlbl_cipsov4_genl_policy) != 0) + return -EINVAL; + + nla_for_each_nested(nla, info->attrs[NLBL_CIPSOV4_A_TAGLST], nla_rem) + if (nla->nla_type == NLBL_CIPSOV4_A_TAG) { + if (iter > CIPSO_V4_TAG_MAXCNT) + return -EINVAL; + doi_def->tags[iter++] = nla_get_u8(nla); + } + if (iter < CIPSO_V4_TAG_MAXCNT) + doi_def->tags[iter] = CIPSO_V4_TAG_INVALID; + + return 0; +} /* * NetLabel Command Handlers @@ -88,9 +145,7 @@ static void netlbl_cipsov4_doi_free(struct rcu_head *entry) /** * netlbl_cipsov4_add_std - Adds a CIPSO V4 DOI definition - * @doi: the DOI value - * @msg: the ADD message data - * @msg_size: the size of the ADD message buffer + * @info: the Generic NETLINK info block * * Description: * Create a new CIPSO_V4_MAP_STD DOI definition based on the given ADD message @@ -98,29 +153,28 @@ static void netlbl_cipsov4_doi_free(struct rcu_head *entry) * error. * */ -static int netlbl_cipsov4_add_std(u32 doi, struct nlattr *msg, size_t msg_size) +static int netlbl_cipsov4_add_std(struct genl_info *info) { int ret_val = -EINVAL; - int msg_len = msg_size; - u32 num_tags; - u32 num_lvls; - u32 num_cats; struct cipso_v4_doi *doi_def = NULL; - u32 iter; - u32 tmp_val_a; - u32 tmp_val_b; + struct nlattr *nla_a; + struct nlattr *nla_b; + int nla_a_rem; + int nla_b_rem; - if (msg_len < NETLBL_LEN_U32) - goto add_std_failure; - num_tags = netlbl_getinc_u32(&msg, &msg_len); - if (num_tags == 0 || num_tags > CIPSO_V4_TAG_MAXCNT) - goto add_std_failure; + if (!info->attrs[NLBL_CIPSOV4_A_DOI] || + !info->attrs[NLBL_CIPSOV4_A_TAGLST] || + !info->attrs[NLBL_CIPSOV4_A_MLSLVLLST]) + return -EINVAL; + + if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_MLSLVLLST], + NLBL_CIPSOV4_A_MAX, + netlbl_cipsov4_genl_policy) != 0) + return -EINVAL; doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL); - if (doi_def == NULL) { - ret_val = -ENOMEM; - goto add_std_failure; - } + if (doi_def == NULL) + return -ENOMEM; doi_def->map.std = kzalloc(sizeof(*doi_def->map.std), GFP_KERNEL); if (doi_def->map.std == NULL) { ret_val = -ENOMEM; @@ -128,28 +182,32 @@ static int netlbl_cipsov4_add_std(u32 doi, struct nlattr *msg, size_t msg_size) } doi_def->type = CIPSO_V4_MAP_STD; - for (iter = 0; iter < num_tags; iter++) { - if (msg_len < NETLBL_LEN_U8) - goto add_std_failure; - doi_def->tags[iter] = netlbl_getinc_u8(&msg, &msg_len); - switch (doi_def->tags[iter]) { - case CIPSO_V4_TAG_RBITMAP: - break; - default: - goto add_std_failure; + ret_val = netlbl_cipsov4_add_common(info, doi_def); + if (ret_val != 0) + goto add_std_failure; + + nla_for_each_nested(nla_a, + info->attrs[NLBL_CIPSOV4_A_MLSLVLLST], + nla_a_rem) + if (nla_a->nla_type == NLBL_CIPSOV4_A_MLSLVL) { + nla_for_each_nested(nla_b, nla_a, nla_b_rem) + switch (nla_b->nla_type) { + case NLBL_CIPSOV4_A_MLSLVLLOC: + if (nla_get_u32(nla_b) >= + doi_def->map.std->lvl.local_size) + doi_def->map.std->lvl.local_size = + nla_get_u32(nla_b) + 1; + break; + case NLBL_CIPSOV4_A_MLSLVLREM: + if (nla_get_u32(nla_b) >= + doi_def->map.std->lvl.cipso_size) + doi_def->map.std->lvl.cipso_size = + nla_get_u32(nla_b) + 1; + break; + } } - } - if (iter < CIPSO_V4_TAG_MAXCNT) - doi_def->tags[iter] = CIPSO_V4_TAG_INVALID; - - if (msg_len < 6 * NETLBL_LEN_U32) - goto add_std_failure; - - num_lvls = netlbl_getinc_u32(&msg, &msg_len); - if (num_lvls == 0) - goto add_std_failure; - doi_def->map.std->lvl.local_size = netlbl_getinc_u32(&msg, &msg_len); - if (doi_def->map.std->lvl.local_size > CIPSO_V4_MAX_LOC_LVLS) + if (doi_def->map.std->lvl.local_size > CIPSO_V4_MAX_LOC_LVLS || + doi_def->map.std->lvl.cipso_size > CIPSO_V4_MAX_REM_LVLS) goto add_std_failure; doi_def->map.std->lvl.local = kcalloc(doi_def->map.std->lvl.local_size, sizeof(u32), @@ -158,9 +216,6 @@ static int netlbl_cipsov4_add_std(u32 doi, struct nlattr *msg, size_t msg_size) ret_val = -ENOMEM; goto add_std_failure; } - doi_def->map.std->lvl.cipso_size = netlbl_getinc_u8(&msg, &msg_len); - if (doi_def->map.std->lvl.cipso_size > CIPSO_V4_MAX_REM_LVLS) - goto add_std_failure; doi_def->map.std->lvl.cipso = kcalloc(doi_def->map.std->lvl.cipso_size, sizeof(u32), GFP_KERNEL); @@ -168,68 +223,101 @@ static int netlbl_cipsov4_add_std(u32 doi, struct nlattr *msg, size_t msg_size) ret_val = -ENOMEM; goto add_std_failure; } + nla_for_each_nested(nla_a, + info->attrs[NLBL_CIPSOV4_A_MLSLVLLST], + nla_a_rem) + if (nla_a->nla_type == NLBL_CIPSOV4_A_MLSLVL) { + struct nlattr *lvl_loc; + struct nlattr *lvl_rem; - num_cats = netlbl_getinc_u32(&msg, &msg_len); - doi_def->map.std->cat.local_size = netlbl_getinc_u32(&msg, &msg_len); - if (doi_def->map.std->cat.local_size > CIPSO_V4_MAX_LOC_CATS) - goto add_std_failure; - doi_def->map.std->cat.local = kcalloc(doi_def->map.std->cat.local_size, - sizeof(u32), - GFP_KERNEL); - if (doi_def->map.std->cat.local == NULL) { - ret_val = -ENOMEM; - goto add_std_failure; - } - doi_def->map.std->cat.cipso_size = netlbl_getinc_u16(&msg, &msg_len); - if (doi_def->map.std->cat.cipso_size > CIPSO_V4_MAX_REM_CATS) - goto add_std_failure; - doi_def->map.std->cat.cipso = kcalloc(doi_def->map.std->cat.cipso_size, - sizeof(u32), - GFP_KERNEL); - if (doi_def->map.std->cat.cipso == NULL) { - ret_val = -ENOMEM; - goto add_std_failure; - } + if (nla_validate_nested(nla_a, + NLBL_CIPSOV4_A_MAX, + netlbl_cipsov4_genl_policy) != 0) + goto add_std_failure; - if (msg_len < - num_lvls * (NETLBL_LEN_U32 + NETLBL_LEN_U8) + - num_cats * (NETLBL_LEN_U32 + NETLBL_LEN_U16)) - goto add_std_failure; + lvl_loc = nla_find_nested(nla_a, + NLBL_CIPSOV4_A_MLSLVLLOC); + lvl_rem = nla_find_nested(nla_a, + NLBL_CIPSOV4_A_MLSLVLREM); + if (lvl_loc == NULL || lvl_rem == NULL) + goto add_std_failure; + doi_def->map.std->lvl.local[nla_get_u32(lvl_loc)] = + nla_get_u32(lvl_rem); + doi_def->map.std->lvl.cipso[nla_get_u32(lvl_rem)] = + nla_get_u32(lvl_loc); + } - for (iter = 0; iter < doi_def->map.std->lvl.cipso_size; iter++) - doi_def->map.std->lvl.cipso[iter] = CIPSO_V4_INV_LVL; - for (iter = 0; iter < doi_def->map.std->lvl.local_size; iter++) - doi_def->map.std->lvl.local[iter] = CIPSO_V4_INV_LVL; - for (iter = 0; iter < doi_def->map.std->cat.cipso_size; iter++) - doi_def->map.std->cat.cipso[iter] = CIPSO_V4_INV_CAT; - for (iter = 0; iter < doi_def->map.std->cat.local_size; iter++) - doi_def->map.std->cat.local[iter] = CIPSO_V4_INV_CAT; - - for (iter = 0; iter < num_lvls; iter++) { - tmp_val_a = netlbl_getinc_u32(&msg, &msg_len); - tmp_val_b = netlbl_getinc_u8(&msg, &msg_len); - - if (tmp_val_a >= doi_def->map.std->lvl.local_size || - tmp_val_b >= doi_def->map.std->lvl.cipso_size) + if (info->attrs[NLBL_CIPSOV4_A_MLSCATLST]) { + if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_MLSCATLST], + NLBL_CIPSOV4_A_MAX, + netlbl_cipsov4_genl_policy) != 0) goto add_std_failure; - doi_def->map.std->lvl.cipso[tmp_val_b] = tmp_val_a; - doi_def->map.std->lvl.local[tmp_val_a] = tmp_val_b; - } - - for (iter = 0; iter < num_cats; iter++) { - tmp_val_a = netlbl_getinc_u32(&msg, &msg_len); - tmp_val_b = netlbl_getinc_u16(&msg, &msg_len); - - if (tmp_val_a >= doi_def->map.std->cat.local_size || - tmp_val_b >= doi_def->map.std->cat.cipso_size) + nla_for_each_nested(nla_a, + info->attrs[NLBL_CIPSOV4_A_MLSCATLST], + nla_a_rem) + if (nla_a->nla_type == NLBL_CIPSOV4_A_MLSCAT) { + if (nla_validate_nested(nla_a, + NLBL_CIPSOV4_A_MAX, + netlbl_cipsov4_genl_policy) != 0) + goto add_std_failure; + nla_for_each_nested(nla_b, nla_a, nla_b_rem) + switch (nla_b->nla_type) { + case NLBL_CIPSOV4_A_MLSCATLOC: + if (nla_get_u32(nla_b) >= + doi_def->map.std->cat.local_size) + doi_def->map.std->cat.local_size = + nla_get_u32(nla_b) + 1; + break; + case NLBL_CIPSOV4_A_MLSCATREM: + if (nla_get_u32(nla_b) >= + doi_def->map.std->cat.cipso_size) + doi_def->map.std->cat.cipso_size = + nla_get_u32(nla_b) + 1; + break; + } + } + if (doi_def->map.std->cat.local_size > CIPSO_V4_MAX_LOC_CATS || + doi_def->map.std->cat.cipso_size > CIPSO_V4_MAX_REM_CATS) goto add_std_failure; + doi_def->map.std->cat.local = kcalloc( + doi_def->map.std->cat.local_size, + sizeof(u32), + GFP_KERNEL); + if (doi_def->map.std->cat.local == NULL) { + ret_val = -ENOMEM; + goto add_std_failure; + } + doi_def->map.std->cat.cipso = kcalloc( + doi_def->map.std->cat.cipso_size, + sizeof(u32), + GFP_KERNEL); + if (doi_def->map.std->cat.cipso == NULL) { + ret_val = -ENOMEM; + goto add_std_failure; + } + nla_for_each_nested(nla_a, + info->attrs[NLBL_CIPSOV4_A_MLSCATLST], + nla_a_rem) + if (nla_a->nla_type == NLBL_CIPSOV4_A_MLSCAT) { + struct nlattr *cat_loc; + struct nlattr *cat_rem; - doi_def->map.std->cat.cipso[tmp_val_b] = tmp_val_a; - doi_def->map.std->cat.local[tmp_val_a] = tmp_val_b; + cat_loc = nla_find_nested(nla_a, + NLBL_CIPSOV4_A_MLSCATLOC); + cat_rem = nla_find_nested(nla_a, + NLBL_CIPSOV4_A_MLSCATREM); + if (cat_loc == NULL || cat_rem == NULL) + goto add_std_failure; + doi_def->map.std->cat.local[ + nla_get_u32(cat_loc)] = + nla_get_u32(cat_rem); + doi_def->map.std->cat.cipso[ + nla_get_u32(cat_rem)] = + nla_get_u32(cat_loc); + } } - doi_def->doi = doi; ret_val = cipso_v4_doi_add(doi_def); if (ret_val != 0) goto add_std_failure; @@ -243,9 +331,7 @@ add_std_failure: /** * netlbl_cipsov4_add_pass - Adds a CIPSO V4 DOI definition - * @doi: the DOI value - * @msg: the ADD message data - * @msg_size: the size of the ADD message buffer + * @info: the Generic NETLINK info block * * Description: * Create a new CIPSO_V4_MAP_PASS DOI definition based on the given ADD message @@ -253,52 +339,31 @@ add_std_failure: * error. * */ -static int netlbl_cipsov4_add_pass(u32 doi, - struct nlattr *msg, - size_t msg_size) +static int netlbl_cipsov4_add_pass(struct genl_info *info) { - int ret_val = -EINVAL; - int msg_len = msg_size; - u32 num_tags; + int ret_val; struct cipso_v4_doi *doi_def = NULL; - u32 iter; - if (msg_len < NETLBL_LEN_U32) - goto add_pass_failure; - num_tags = netlbl_getinc_u32(&msg, &msg_len); - if (num_tags == 0 || num_tags > CIPSO_V4_TAG_MAXCNT) - goto add_pass_failure; + if (!info->attrs[NLBL_CIPSOV4_A_DOI] || + !info->attrs[NLBL_CIPSOV4_A_TAGLST]) + return -EINVAL; doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL); - if (doi_def == NULL) { - ret_val = -ENOMEM; - goto add_pass_failure; - } + if (doi_def == NULL) + return -ENOMEM; doi_def->type = CIPSO_V4_MAP_PASS; - for (iter = 0; iter < num_tags; iter++) { - if (msg_len < NETLBL_LEN_U8) - goto add_pass_failure; - doi_def->tags[iter] = netlbl_getinc_u8(&msg, &msg_len); - switch (doi_def->tags[iter]) { - case CIPSO_V4_TAG_RBITMAP: - break; - default: - goto add_pass_failure; - } - } - if (iter < CIPSO_V4_TAG_MAXCNT) - doi_def->tags[iter] = CIPSO_V4_TAG_INVALID; + ret_val = netlbl_cipsov4_add_common(info, doi_def); + if (ret_val != 0) + goto add_pass_failure; - doi_def->doi = doi; ret_val = cipso_v4_doi_add(doi_def); if (ret_val != 0) goto add_pass_failure; return 0; add_pass_failure: - if (doi_def) - netlbl_cipsov4_doi_free(&doi_def->rcu); + netlbl_cipsov4_doi_free(&doi_def->rcu); return ret_val; } @@ -316,34 +381,21 @@ static int netlbl_cipsov4_add(struct sk_buff *skb, struct genl_info *info) { int ret_val = -EINVAL; - u32 doi; u32 map_type; - int msg_len = netlbl_netlink_payload_len(skb); - struct nlattr *msg = netlbl_netlink_payload_data(skb); - ret_val = netlbl_netlink_cap_check(skb, CAP_NET_ADMIN); - if (ret_val != 0) - goto add_return; + if (!info->attrs[NLBL_CIPSOV4_A_MTYPE]) + return -EINVAL; - if (msg_len < 2 * NETLBL_LEN_U32) - goto add_return; - - doi = netlbl_getinc_u32(&msg, &msg_len); - map_type = netlbl_getinc_u32(&msg, &msg_len); + map_type = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE]); switch (map_type) { case CIPSO_V4_MAP_STD: - ret_val = netlbl_cipsov4_add_std(doi, msg, msg_len); + ret_val = netlbl_cipsov4_add_std(info); break; case CIPSO_V4_MAP_PASS: - ret_val = netlbl_cipsov4_add_pass(doi, msg, msg_len); + ret_val = netlbl_cipsov4_add_pass(info); break; } -add_return: - netlbl_netlink_send_ack(info, - netlbl_cipsov4_gnl_family.id, - NLBL_CIPSOV4_C_ACK, - -ret_val); return ret_val; } @@ -353,84 +405,239 @@ add_return: * @info: the Generic NETLINK info block * * Description: - * Process a user generated LIST message and respond accordingly. Returns - * zero on success and negative values on error. + * Process a user generated LIST message and respond accordingly. While the + * response message generated by the kernel is straightforward, determining + * before hand the size of the buffer to allocate is not (we have to generate + * the message to know the size). In order to keep this function sane what we + * do is allocate a buffer of NLMSG_GOODSIZE and try to fit the response in + * that size, if we fail then we restart with a larger buffer and try again. + * We continue in this manner until we hit a limit of failed attempts then we + * give up and just send an error message. Returns zero on success and + * negative values on error. * */ static int netlbl_cipsov4_list(struct sk_buff *skb, struct genl_info *info) { - int ret_val = -EINVAL; + int ret_val; + struct sk_buff *ans_skb = NULL; + u32 nlsze_mult = 1; + void *data; u32 doi; - struct nlattr *msg = netlbl_netlink_payload_data(skb); - struct sk_buff *ans_skb; + struct nlattr *nla_a; + struct nlattr *nla_b; + struct cipso_v4_doi *doi_def; + u32 iter; - if (netlbl_netlink_payload_len(skb) != NETLBL_LEN_U32) + if (!info->attrs[NLBL_CIPSOV4_A_DOI]) { + ret_val = -EINVAL; goto list_failure; + } - doi = nla_get_u32(msg); - ans_skb = cipso_v4_doi_dump(doi, NLMSG_SPACE(GENL_HDRLEN)); +list_start: + ans_skb = nlmsg_new(NLMSG_GOODSIZE * nlsze_mult, GFP_KERNEL); if (ans_skb == NULL) { ret_val = -ENOMEM; goto list_failure; } - netlbl_netlink_hdr_push(ans_skb, - info->snd_pid, - 0, - netlbl_cipsov4_gnl_family.id, - NLBL_CIPSOV4_C_LIST); + data = netlbl_netlink_hdr_put(ans_skb, + info->snd_pid, + info->snd_seq, + netlbl_cipsov4_gnl_family.id, + 0, + NLBL_CIPSOV4_C_LIST); + if (data == NULL) { + ret_val = -ENOMEM; + goto list_failure; + } - ret_val = netlbl_netlink_snd(ans_skb, info->snd_pid); + doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]); + + rcu_read_lock(); + doi_def = cipso_v4_doi_getdef(doi); + if (doi_def == NULL) { + ret_val = -EINVAL; + goto list_failure; + } + + ret_val = nla_put_u32(ans_skb, NLBL_CIPSOV4_A_MTYPE, doi_def->type); + if (ret_val != 0) + goto list_failure_lock; + + nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_TAGLST); + if (nla_a == NULL) { + ret_val = -ENOMEM; + goto list_failure_lock; + } + for (iter = 0; + iter < CIPSO_V4_TAG_MAXCNT && + doi_def->tags[iter] != CIPSO_V4_TAG_INVALID; + iter++) { + ret_val = nla_put_u8(ans_skb, + NLBL_CIPSOV4_A_TAG, + doi_def->tags[iter]); + if (ret_val != 0) + goto list_failure_lock; + } + nla_nest_end(ans_skb, nla_a); + + switch (doi_def->type) { + case CIPSO_V4_MAP_STD: + nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSLVLLST); + if (nla_a == NULL) { + ret_val = -ENOMEM; + goto list_failure_lock; + } + for (iter = 0; + iter < doi_def->map.std->lvl.local_size; + iter++) { + if (doi_def->map.std->lvl.local[iter] == + CIPSO_V4_INV_LVL) + continue; + + nla_b = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSLVL); + if (nla_b == NULL) { + ret_val = -ENOMEM; + goto list_retry; + } + ret_val = nla_put_u32(ans_skb, + NLBL_CIPSOV4_A_MLSLVLLOC, + iter); + if (ret_val != 0) + goto list_retry; + ret_val = nla_put_u32(ans_skb, + NLBL_CIPSOV4_A_MLSLVLREM, + doi_def->map.std->lvl.local[iter]); + if (ret_val != 0) + goto list_retry; + nla_nest_end(ans_skb, nla_b); + } + nla_nest_end(ans_skb, nla_a); + + nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSCATLST); + if (nla_a == NULL) { + ret_val = -ENOMEM; + goto list_retry; + } + for (iter = 0; + iter < doi_def->map.std->cat.local_size; + iter++) { + if (doi_def->map.std->cat.local[iter] == + CIPSO_V4_INV_CAT) + continue; + + nla_b = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSCAT); + if (nla_b == NULL) { + ret_val = -ENOMEM; + goto list_retry; + } + ret_val = nla_put_u32(ans_skb, + NLBL_CIPSOV4_A_MLSCATLOC, + iter); + if (ret_val != 0) + goto list_retry; + ret_val = nla_put_u32(ans_skb, + NLBL_CIPSOV4_A_MLSCATREM, + doi_def->map.std->cat.local[iter]); + if (ret_val != 0) + goto list_retry; + nla_nest_end(ans_skb, nla_b); + } + nla_nest_end(ans_skb, nla_a); + + break; + } + rcu_read_unlock(); + + genlmsg_end(ans_skb, data); + + ret_val = genlmsg_unicast(ans_skb, info->snd_pid); if (ret_val != 0) goto list_failure; return 0; +list_retry: + /* XXX - this limit is a guesstimate */ + if (nlsze_mult < 4) { + rcu_read_unlock(); + kfree_skb(ans_skb); + nlsze_mult++; + goto list_start; + } +list_failure_lock: + rcu_read_unlock(); list_failure: - netlbl_netlink_send_ack(info, - netlbl_cipsov4_gnl_family.id, - NLBL_CIPSOV4_C_ACK, - -ret_val); + kfree_skb(ans_skb); + return ret_val; +} + +/** + * netlbl_cipsov4_listall_cb - cipso_v4_doi_walk() callback for LISTALL + * @doi_def: the CIPSOv4 DOI definition + * @arg: the netlbl_cipsov4_doiwalk_arg structure + * + * Description: + * This function is designed to be used as a callback to the + * cipso_v4_doi_walk() function for use in generating a response for a LISTALL + * message. Returns the size of the message on success, negative values on + * failure. + * + */ +static int netlbl_cipsov4_listall_cb(struct cipso_v4_doi *doi_def, void *arg) +{ + int ret_val = -ENOMEM; + struct netlbl_cipsov4_doiwalk_arg *cb_arg = arg; + void *data; + + data = netlbl_netlink_hdr_put(cb_arg->skb, + NETLINK_CB(cb_arg->nl_cb->skb).pid, + cb_arg->seq, + netlbl_cipsov4_gnl_family.id, + NLM_F_MULTI, + NLBL_CIPSOV4_C_LISTALL); + if (data == NULL) + goto listall_cb_failure; + + ret_val = nla_put_u32(cb_arg->skb, NLBL_CIPSOV4_A_DOI, doi_def->doi); + if (ret_val != 0) + goto listall_cb_failure; + ret_val = nla_put_u32(cb_arg->skb, + NLBL_CIPSOV4_A_MTYPE, + doi_def->type); + if (ret_val != 0) + goto listall_cb_failure; + + return genlmsg_end(cb_arg->skb, data); + +listall_cb_failure: + genlmsg_cancel(cb_arg->skb, data); return ret_val; } /** * netlbl_cipsov4_listall - Handle a LISTALL message * @skb: the NETLINK buffer - * @info: the Generic NETLINK info block + * @cb: the NETLINK callback * * Description: * Process a user generated LISTALL message and respond accordingly. Returns * zero on success and negative values on error. * */ -static int netlbl_cipsov4_listall(struct sk_buff *skb, struct genl_info *info) +static int netlbl_cipsov4_listall(struct sk_buff *skb, + struct netlink_callback *cb) { - int ret_val = -EINVAL; - struct sk_buff *ans_skb; + struct netlbl_cipsov4_doiwalk_arg cb_arg; + int doi_skip = cb->args[0]; - ans_skb = cipso_v4_doi_dump_all(NLMSG_SPACE(GENL_HDRLEN)); - if (ans_skb == NULL) { - ret_val = -ENOMEM; - goto listall_failure; - } - netlbl_netlink_hdr_push(ans_skb, - info->snd_pid, - 0, - netlbl_cipsov4_gnl_family.id, - NLBL_CIPSOV4_C_LISTALL); + cb_arg.nl_cb = cb; + cb_arg.skb = skb; + cb_arg.seq = cb->nlh->nlmsg_seq; - ret_val = netlbl_netlink_snd(ans_skb, info->snd_pid); - if (ret_val != 0) - goto listall_failure; + cipso_v4_doi_walk(&doi_skip, netlbl_cipsov4_listall_cb, &cb_arg); - return 0; - -listall_failure: - netlbl_netlink_send_ack(info, - netlbl_cipsov4_gnl_family.id, - NLBL_CIPSOV4_C_ACK, - -ret_val); - return ret_val; + cb->args[0] = doi_skip; + return skb->len; } /** @@ -445,27 +652,14 @@ listall_failure: */ static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info) { - int ret_val; + int ret_val = -EINVAL; u32 doi; - struct nlattr *msg = netlbl_netlink_payload_data(skb); - ret_val = netlbl_netlink_cap_check(skb, CAP_NET_ADMIN); - if (ret_val != 0) - goto remove_return; - - if (netlbl_netlink_payload_len(skb) != NETLBL_LEN_U32) { - ret_val = -EINVAL; - goto remove_return; + if (info->attrs[NLBL_CIPSOV4_A_DOI]) { + doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]); + ret_val = cipso_v4_doi_remove(doi, netlbl_cipsov4_doi_free); } - doi = nla_get_u32(msg); - ret_val = cipso_v4_doi_remove(doi, netlbl_cipsov4_doi_free); - -remove_return: - netlbl_netlink_send_ack(info, - netlbl_cipsov4_gnl_family.id, - NLBL_CIPSOV4_C_ACK, - -ret_val); return ret_val; } @@ -475,14 +669,16 @@ remove_return: static struct genl_ops netlbl_cipsov4_genl_c_add = { .cmd = NLBL_CIPSOV4_C_ADD, - .flags = 0, + .flags = GENL_ADMIN_PERM, + .policy = netlbl_cipsov4_genl_policy, .doit = netlbl_cipsov4_add, .dumpit = NULL, }; static struct genl_ops netlbl_cipsov4_genl_c_remove = { .cmd = NLBL_CIPSOV4_C_REMOVE, - .flags = 0, + .flags = GENL_ADMIN_PERM, + .policy = netlbl_cipsov4_genl_policy, .doit = netlbl_cipsov4_remove, .dumpit = NULL, }; @@ -490,6 +686,7 @@ static struct genl_ops netlbl_cipsov4_genl_c_remove = { static struct genl_ops netlbl_cipsov4_genl_c_list = { .cmd = NLBL_CIPSOV4_C_LIST, .flags = 0, + .policy = netlbl_cipsov4_genl_policy, .doit = netlbl_cipsov4_list, .dumpit = NULL, }; @@ -497,8 +694,9 @@ static struct genl_ops netlbl_cipsov4_genl_c_list = { static struct genl_ops netlbl_cipsov4_genl_c_listall = { .cmd = NLBL_CIPSOV4_C_LISTALL, .flags = 0, - .doit = netlbl_cipsov4_listall, - .dumpit = NULL, + .policy = netlbl_cipsov4_genl_policy, + .doit = NULL, + .dumpit = netlbl_cipsov4_listall, }; /* diff --git a/net/netlabel/netlabel_cipso_v4.h b/net/netlabel/netlabel_cipso_v4.h index 4c6ff4b93004..f03cf9b78286 100644 --- a/net/netlabel/netlabel_cipso_v4.h +++ b/net/netlabel/netlabel_cipso_v4.h @@ -34,175 +34,71 @@ #include /* - * The following NetLabel payloads are supported by the CIPSO subsystem, all - * of which are preceeded by the nlmsghdr struct. - * - * o ACK: - * Sent by the kernel in response to an applications message, applications - * should never send this message. - * - * +----------------------+-----------------------+ - * | seq number (32 bits) | return code (32 bits) | - * +----------------------+-----------------------+ - * - * seq number: the sequence number of the original message, taken from the - * nlmsghdr structure - * return code: return value, based on errno values + * The following NetLabel payloads are supported by the CIPSO subsystem. * * o ADD: - * Sent by an application to add a new DOI mapping table, after completion - * of the task the kernel should ACK this message. + * Sent by an application to add a new DOI mapping table. * - * +---------------+--------------------+---------------------+ - * | DOI (32 bits) | map type (32 bits) | tag count (32 bits) | ... - * +---------------+--------------------+---------------------+ + * Required attributes: * - * +-----------------+ - * | tag #X (8 bits) | ... repeated - * +-----------------+ + * NLBL_CIPSOV4_A_DOI + * NLBL_CIPSOV4_A_MTYPE + * NLBL_CIPSOV4_A_TAGLST * - * +-------------- ---- --- -- - - * | mapping data - * +-------------- ---- --- -- - + * If using CIPSO_V4_MAP_STD the following attributes are required: * - * DOI: the DOI value - * map type: the mapping table type (defined in the cipso_ipv4.h header - * as CIPSO_V4_MAP_*) - * tag count: the number of tags, must be greater than zero - * tag: the CIPSO tag for the DOI, tags listed first are given - * higher priorirty when sending packets - * mapping data: specific to the map type (see below) + * NLBL_CIPSOV4_A_MLSLVLLST + * NLBL_CIPSOV4_A_MLSCATLST * - * CIPSO_V4_MAP_STD - * - * +------------------+-----------------------+----------------------+ - * | levels (32 bits) | max l level (32 bits) | max r level (8 bits) | ... - * +------------------+-----------------------+----------------------+ - * - * +----------------------+---------------------+---------------------+ - * | categories (32 bits) | max l cat (32 bits) | max r cat (16 bits) | ... - * +----------------------+---------------------+---------------------+ - * - * +--------------------------+-------------------------+ - * | local level #X (32 bits) | CIPSO level #X (8 bits) | ... repeated - * +--------------------------+-------------------------+ - * - * +-----------------------------+-----------------------------+ - * | local category #X (32 bits) | CIPSO category #X (16 bits) | ... repeated - * +-----------------------------+-----------------------------+ - * - * levels: the number of level mappings - * max l level: the highest local level - * max r level: the highest remote/CIPSO level - * categories: the number of category mappings - * max l cat: the highest local category - * max r cat: the highest remote/CIPSO category - * local level: the local part of a level mapping - * CIPSO level: the remote/CIPSO part of a level mapping - * local category: the local part of a category mapping - * CIPSO category: the remote/CIPSO part of a category mapping - * - * CIPSO_V4_MAP_PASS - * - * No mapping data is needed for this map type. + * If using CIPSO_V4_MAP_PASS no additional attributes are required. * * o REMOVE: * Sent by an application to remove a specific DOI mapping table from the - * CIPSO V4 system. The kernel should ACK this message. + * CIPSO V4 system. * - * +---------------+ - * | DOI (32 bits) | - * +---------------+ + * Required attributes: * - * DOI: the DOI value + * NLBL_CIPSOV4_A_DOI * * o LIST: - * Sent by an application to list the details of a DOI definition. The - * kernel should send an ACK on error or a response as indicated below. The - * application generated message format is shown below. + * Sent by an application to list the details of a DOI definition. On + * success the kernel should send a response using the following format. * - * +---------------+ - * | DOI (32 bits) | - * +---------------+ + * Required attributes: * - * DOI: the DOI value + * NLBL_CIPSOV4_A_DOI * * The valid response message format depends on the type of the DOI mapping, - * the known formats are shown below. + * the defined formats are shown below. * - * +--------------------+ - * | map type (32 bits) | ... - * +--------------------+ + * Required attributes: * - * map type: the DOI mapping table type (defined in the cipso_ipv4.h - * header as CIPSO_V4_MAP_*) + * NLBL_CIPSOV4_A_MTYPE + * NLBL_CIPSOV4_A_TAGLST * - * (map type == CIPSO_V4_MAP_STD) + * If using CIPSO_V4_MAP_STD the following attributes are required: * - * +----------------+------------------+----------------------+ - * | tags (32 bits) | levels (32 bits) | categories (32 bits) | ... - * +----------------+------------------+----------------------+ + * NLBL_CIPSOV4_A_MLSLVLLST + * NLBL_CIPSOV4_A_MLSCATLST * - * +-----------------+ - * | tag #X (8 bits) | ... repeated - * +-----------------+ - * - * +--------------------------+-------------------------+ - * | local level #X (32 bits) | CIPSO level #X (8 bits) | ... repeated - * +--------------------------+-------------------------+ - * - * +-----------------------------+-----------------------------+ - * | local category #X (32 bits) | CIPSO category #X (16 bits) | ... repeated - * +-----------------------------+-----------------------------+ - * - * tags: the number of CIPSO tag types - * levels: the number of level mappings - * categories: the number of category mappings - * tag: the tag number, tags listed first are given higher - * priority when sending packets - * local level: the local part of a level mapping - * CIPSO level: the remote/CIPSO part of a level mapping - * local category: the local part of a category mapping - * CIPSO category: the remote/CIPSO part of a category mapping - * - * (map type == CIPSO_V4_MAP_PASS) - * - * +----------------+ - * | tags (32 bits) | ... - * +----------------+ - * - * +-----------------+ - * | tag #X (8 bits) | ... repeated - * +-----------------+ - * - * tags: the number of CIPSO tag types - * tag: the tag number, tags listed first are given higher - * priority when sending packets + * If using CIPSO_V4_MAP_PASS no additional attributes are required. * * o LISTALL: * This message is sent by an application to list the valid DOIs on the - * system. There is no payload and the kernel should respond with an ACK - * or the following message. + * system. When sent by an application there is no payload and the + * NLM_F_DUMP flag should be set. The kernel should respond with a series of + * the following messages. * - * +---------------------+------------------+-----------------------+ - * | DOI count (32 bits) | DOI #X (32 bits) | map type #X (32 bits) | - * +---------------------+------------------+-----------------------+ + * Required attributes: * - * +-----------------------+ - * | map type #X (32 bits) | ... - * +-----------------------+ - * - * DOI count: the number of DOIs - * DOI: the DOI value - * map type: the DOI mapping table type (defined in the cipso_ipv4.h - * header as CIPSO_V4_MAP_*) + * NLBL_CIPSOV4_A_DOI + * NLBL_CIPSOV4_A_MTYPE * */ /* NetLabel CIPSOv4 commands */ enum { NLBL_CIPSOV4_C_UNSPEC, - NLBL_CIPSOV4_C_ACK, NLBL_CIPSOV4_C_ADD, NLBL_CIPSOV4_C_REMOVE, NLBL_CIPSOV4_C_LIST, @@ -211,6 +107,59 @@ enum { }; #define NLBL_CIPSOV4_C_MAX (__NLBL_CIPSOV4_C_MAX - 1) +/* NetLabel CIPSOv4 attributes */ +enum { + NLBL_CIPSOV4_A_UNSPEC, + NLBL_CIPSOV4_A_DOI, + /* (NLA_U32) + * the DOI value */ + NLBL_CIPSOV4_A_MTYPE, + /* (NLA_U32) + * the mapping table type (defined in the cipso_ipv4.h header as + * CIPSO_V4_MAP_*) */ + NLBL_CIPSOV4_A_TAG, + /* (NLA_U8) + * a CIPSO tag type, meant to be used within a NLBL_CIPSOV4_A_TAGLST + * attribute */ + NLBL_CIPSOV4_A_TAGLST, + /* (NLA_NESTED) + * the CIPSO tag list for the DOI, there must be at least one + * NLBL_CIPSOV4_A_TAG attribute, tags listed first are given higher + * priorirty when sending packets */ + NLBL_CIPSOV4_A_MLSLVLLOC, + /* (NLA_U32) + * the local MLS sensitivity level */ + NLBL_CIPSOV4_A_MLSLVLREM, + /* (NLA_U32) + * the remote MLS sensitivity level */ + NLBL_CIPSOV4_A_MLSLVL, + /* (NLA_NESTED) + * a MLS sensitivity level mapping, must contain only one attribute of + * each of the following types: NLBL_CIPSOV4_A_MLSLVLLOC and + * NLBL_CIPSOV4_A_MLSLVLREM */ + NLBL_CIPSOV4_A_MLSLVLLST, + /* (NLA_NESTED) + * the CIPSO level mappings, there must be at least one + * NLBL_CIPSOV4_A_MLSLVL attribute */ + NLBL_CIPSOV4_A_MLSCATLOC, + /* (NLA_U32) + * the local MLS category */ + NLBL_CIPSOV4_A_MLSCATREM, + /* (NLA_U32) + * the remote MLS category */ + NLBL_CIPSOV4_A_MLSCAT, + /* (NLA_NESTED) + * a MLS category mapping, must contain only one attribute of each of + * the following types: NLBL_CIPSOV4_A_MLSCATLOC and + * NLBL_CIPSOV4_A_MLSCATREM */ + NLBL_CIPSOV4_A_MLSCATLST, + /* (NLA_NESTED) + * the CIPSO category mappings, there must be at least one + * NLBL_CIPSOV4_A_MLSCAT attribute */ + __NLBL_CIPSOV4_A_MAX, +}; +#define NLBL_CIPSOV4_A_MAX (__NLBL_CIPSOV4_A_MAX - 1) + /* NetLabel protocol functions */ int netlbl_cipsov4_genl_init(void); diff --git a/net/netlabel/netlabel_domainhash.c b/net/netlabel/netlabel_domainhash.c index 0489a1378101..f56d7a8ac7b7 100644 --- a/net/netlabel/netlabel_domainhash.c +++ b/net/netlabel/netlabel_domainhash.c @@ -354,160 +354,51 @@ struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain) } /** - * netlbl_domhsh_dump - Dump the domain hash table into a sk_buff + * netlbl_domhsh_walk - Iterate through the domain mapping hash table + * @skip_bkt: the number of buckets to skip at the start + * @skip_chain: the number of entries to skip in the first iterated bucket + * @callback: callback for each entry + * @cb_arg: argument for the callback function * * Description: - * Dump the domain hash table into a buffer suitable for returning to an - * application in response to a NetLabel management DOMAIN message. This - * function may fail if another process is growing the hash table at the same - * time. The returned sk_buff has room at the front of the sk_buff for - * @headroom bytes. See netlabel.h for the DOMAIN message format. Returns a - * pointer to a sk_buff on success, NULL on error. + * Interate over the domain mapping hash table, skipping the first @skip_bkt + * buckets and @skip_chain entries. For each entry in the table call + * @callback, if @callback returns a negative value stop 'walking' through the + * table and return. Updates the values in @skip_bkt and @skip_chain on + * return. Returns zero on succcess, negative values on failure. * */ -struct sk_buff *netlbl_domhsh_dump(size_t headroom) +int netlbl_domhsh_walk(u32 *skip_bkt, + u32 *skip_chain, + int (*callback) (struct netlbl_dom_map *entry, void *arg), + void *cb_arg) { - struct sk_buff *skb = NULL; - ssize_t buf_len; - u32 bkt_iter; - u32 dom_cnt = 0; - struct netlbl_domhsh_tbl *hsh_tbl; - struct netlbl_dom_map *list_iter; - ssize_t tmp_len; + int ret_val = -ENOENT; + u32 iter_bkt; + struct netlbl_dom_map *iter_entry; + u32 chain_cnt = 0; - buf_len = NETLBL_LEN_U32; rcu_read_lock(); - hsh_tbl = rcu_dereference(netlbl_domhsh); - for (bkt_iter = 0; bkt_iter < hsh_tbl->size; bkt_iter++) - list_for_each_entry_rcu(list_iter, - &hsh_tbl->tbl[bkt_iter], list) { - buf_len += NETLBL_LEN_U32 + - nla_total_size(strlen(list_iter->domain) + 1); - switch (list_iter->type) { - case NETLBL_NLTYPE_UNLABELED: - break; - case NETLBL_NLTYPE_CIPSOV4: - buf_len += 2 * NETLBL_LEN_U32; - break; + for (iter_bkt = *skip_bkt; + iter_bkt < rcu_dereference(netlbl_domhsh)->size; + iter_bkt++, chain_cnt = 0) { + list_for_each_entry_rcu(iter_entry, + &netlbl_domhsh->tbl[iter_bkt], + list) + if (iter_entry->valid) { + if (chain_cnt++ < *skip_chain) + continue; + ret_val = callback(iter_entry, cb_arg); + if (ret_val < 0) { + chain_cnt--; + goto walk_return; + } } - dom_cnt++; - } + } - skb = netlbl_netlink_alloc_skb(headroom, buf_len, GFP_ATOMIC); - if (skb == NULL) - goto dump_failure; - - if (nla_put_u32(skb, NLA_U32, dom_cnt) != 0) - goto dump_failure; - buf_len -= NETLBL_LEN_U32; - hsh_tbl = rcu_dereference(netlbl_domhsh); - for (bkt_iter = 0; bkt_iter < hsh_tbl->size; bkt_iter++) - list_for_each_entry_rcu(list_iter, - &hsh_tbl->tbl[bkt_iter], list) { - tmp_len = nla_total_size(strlen(list_iter->domain) + - 1); - if (buf_len < NETLBL_LEN_U32 + tmp_len) - goto dump_failure; - if (nla_put_string(skb, - NLA_STRING, - list_iter->domain) != 0) - goto dump_failure; - if (nla_put_u32(skb, NLA_U32, list_iter->type) != 0) - goto dump_failure; - buf_len -= NETLBL_LEN_U32 + tmp_len; - switch (list_iter->type) { - case NETLBL_NLTYPE_UNLABELED: - break; - case NETLBL_NLTYPE_CIPSOV4: - if (buf_len < 2 * NETLBL_LEN_U32) - goto dump_failure; - if (nla_put_u32(skb, - NLA_U32, - list_iter->type_def.cipsov4->type) != 0) - goto dump_failure; - if (nla_put_u32(skb, - NLA_U32, - list_iter->type_def.cipsov4->doi) != 0) - goto dump_failure; - buf_len -= 2 * NETLBL_LEN_U32; - break; - } - } +walk_return: rcu_read_unlock(); - - return skb; - -dump_failure: - rcu_read_unlock(); - kfree_skb(skb); - return NULL; -} - -/** - * netlbl_domhsh_dump_default - Dump the default domain mapping into a sk_buff - * - * Description: - * Dump the default domain mapping into a buffer suitable for returning to an - * application in response to a NetLabel management DEFDOMAIN message. This - * function may fail if another process is changing the default domain mapping - * at the same time. The returned sk_buff has room at the front of the - * skb_buff for @headroom bytes. See netlabel.h for the DEFDOMAIN message - * format. Returns a pointer to a sk_buff on success, NULL on error. - * - */ -struct sk_buff *netlbl_domhsh_dump_default(size_t headroom) -{ - struct sk_buff *skb; - ssize_t buf_len; - struct netlbl_dom_map *entry; - - buf_len = NETLBL_LEN_U32; - rcu_read_lock(); - entry = rcu_dereference(netlbl_domhsh_def); - if (entry != NULL) - switch (entry->type) { - case NETLBL_NLTYPE_UNLABELED: - break; - case NETLBL_NLTYPE_CIPSOV4: - buf_len += 2 * NETLBL_LEN_U32; - break; - } - - skb = netlbl_netlink_alloc_skb(headroom, buf_len, GFP_ATOMIC); - if (skb == NULL) - goto dump_default_failure; - - if (entry != rcu_dereference(netlbl_domhsh_def)) - goto dump_default_failure; - if (entry != NULL) { - if (nla_put_u32(skb, NLA_U32, entry->type) != 0) - goto dump_default_failure; - buf_len -= NETLBL_LEN_U32; - switch (entry->type) { - case NETLBL_NLTYPE_UNLABELED: - break; - case NETLBL_NLTYPE_CIPSOV4: - if (buf_len < 2 * NETLBL_LEN_U32) - goto dump_default_failure; - if (nla_put_u32(skb, - NLA_U32, - entry->type_def.cipsov4->type) != 0) - goto dump_default_failure; - if (nla_put_u32(skb, - NLA_U32, - entry->type_def.cipsov4->doi) != 0) - goto dump_default_failure; - buf_len -= 2 * NETLBL_LEN_U32; - break; - } - } else - nla_put_u32(skb, NLA_U32, NETLBL_NLTYPE_NONE); - rcu_read_unlock(); - - return skb; - -dump_default_failure: - rcu_read_unlock(); - kfree_skb(skb); - return NULL; + *skip_bkt = iter_bkt; + *skip_chain = chain_cnt; + return ret_val; } diff --git a/net/netlabel/netlabel_domainhash.h b/net/netlabel/netlabel_domainhash.h index 99a2287de246..02af72a7877c 100644 --- a/net/netlabel/netlabel_domainhash.h +++ b/net/netlabel/netlabel_domainhash.h @@ -61,7 +61,9 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry); int netlbl_domhsh_add_default(struct netlbl_dom_map *entry); int netlbl_domhsh_remove_default(void); struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain); -struct sk_buff *netlbl_domhsh_dump(size_t headroom); -struct sk_buff *netlbl_domhsh_dump_default(size_t headroom); +int netlbl_domhsh_walk(u32 *skip_bkt, + u32 *skip_chain, + int (*callback) (struct netlbl_dom_map *entry, void *arg), + void *cb_arg); #endif diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index 0fd8aaafe23f..54fb7de3c2b1 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -84,6 +84,29 @@ socket_setattr_return: return ret_val; } +/** + * netlbl_sock_getattr - Determine the security attributes of a sock + * @sk: the sock + * @secattr: the security attributes + * + * Description: + * Examines the given sock to see any NetLabel style labeling has been + * applied to the sock, if so it parses the socket label and returns the + * security attributes in @secattr. Returns zero on success, negative values + * on failure. + * + */ +int netlbl_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr) +{ + int ret_val; + + ret_val = cipso_v4_sock_getattr(sk, secattr); + if (ret_val == 0) + return 0; + + return netlbl_unlabel_getattr(secattr); +} + /** * netlbl_socket_getattr - Determine the security attributes of a socket * @sock: the socket diff --git a/net/netlabel/netlabel_mgmt.c b/net/netlabel/netlabel_mgmt.c index 85bc11a1fc46..8626c9f678eb 100644 --- a/net/netlabel/netlabel_mgmt.c +++ b/net/netlabel/netlabel_mgmt.c @@ -42,15 +42,29 @@ #include "netlabel_user.h" #include "netlabel_mgmt.h" +/* Argument struct for netlbl_domhsh_walk() */ +struct netlbl_domhsh_walk_arg { + struct netlink_callback *nl_cb; + struct sk_buff *skb; + u32 seq; +}; + /* NetLabel Generic NETLINK CIPSOv4 family */ static struct genl_family netlbl_mgmt_gnl_family = { .id = GENL_ID_GENERATE, .hdrsize = 0, .name = NETLBL_NLTYPE_MGMT_NAME, .version = NETLBL_PROTO_VERSION, - .maxattr = 0, + .maxattr = NLBL_MGMT_A_MAX, }; +/* NetLabel Netlink attribute policy */ +static struct nla_policy netlbl_mgmt_genl_policy[NLBL_MGMT_A_MAX + 1] = { + [NLBL_MGMT_A_DOMAIN] = { .type = NLA_NUL_STRING }, + [NLBL_MGMT_A_PROTOCOL] = { .type = NLA_U32 }, + [NLBL_MGMT_A_VERSION] = { .type = NLA_U32 }, + [NLBL_MGMT_A_CV4DOI] = { .type = NLA_U32 }, +}; /* * NetLabel Command Handlers @@ -70,97 +84,62 @@ static struct genl_family netlbl_mgmt_gnl_family = { static int netlbl_mgmt_add(struct sk_buff *skb, struct genl_info *info) { int ret_val = -EINVAL; - struct nlattr *msg_ptr = netlbl_netlink_payload_data(skb); - int msg_len = netlbl_netlink_payload_len(skb); - u32 count; struct netlbl_dom_map *entry = NULL; - u32 iter; + size_t tmp_size; u32 tmp_val; - int tmp_size; - ret_val = netlbl_netlink_cap_check(skb, CAP_NET_ADMIN); + if (!info->attrs[NLBL_MGMT_A_DOMAIN] || + !info->attrs[NLBL_MGMT_A_PROTOCOL]) + goto add_failure; + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (entry == NULL) { + ret_val = -ENOMEM; + goto add_failure; + } + tmp_size = nla_len(info->attrs[NLBL_MGMT_A_DOMAIN]); + entry->domain = kmalloc(tmp_size, GFP_KERNEL); + if (entry->domain == NULL) { + ret_val = -ENOMEM; + goto add_failure; + } + entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]); + nla_strlcpy(entry->domain, info->attrs[NLBL_MGMT_A_DOMAIN], tmp_size); + + switch (entry->type) { + case NETLBL_NLTYPE_UNLABELED: + ret_val = netlbl_domhsh_add(entry); + break; + case NETLBL_NLTYPE_CIPSOV4: + if (!info->attrs[NLBL_MGMT_A_CV4DOI]) + goto add_failure; + + tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]); + /* We should be holding a rcu_read_lock() here while we hold + * the result but since the entry will always be deleted when + * the CIPSO DOI is deleted we aren't going to keep the + * lock. */ + rcu_read_lock(); + entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val); + if (entry->type_def.cipsov4 == NULL) { + rcu_read_unlock(); + goto add_failure; + } + ret_val = netlbl_domhsh_add(entry); + rcu_read_unlock(); + break; + default: + goto add_failure; + } if (ret_val != 0) goto add_failure; - if (msg_len < NETLBL_LEN_U32) - goto add_failure; - count = netlbl_getinc_u32(&msg_ptr, &msg_len); - - for (iter = 0; iter < count && msg_len > 0; iter++, entry = NULL) { - if (msg_len <= 0) { - ret_val = -EINVAL; - goto add_failure; - } - entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (entry == NULL) { - ret_val = -ENOMEM; - goto add_failure; - } - tmp_size = nla_len(msg_ptr); - if (tmp_size <= 0 || tmp_size > msg_len) { - ret_val = -EINVAL; - goto add_failure; - } - entry->domain = kmalloc(tmp_size, GFP_KERNEL); - if (entry->domain == NULL) { - ret_val = -ENOMEM; - goto add_failure; - } - nla_strlcpy(entry->domain, msg_ptr, tmp_size); - entry->domain[tmp_size - 1] = '\0'; - msg_ptr = nla_next(msg_ptr, &msg_len); - - if (msg_len < NETLBL_LEN_U32) { - ret_val = -EINVAL; - goto add_failure; - } - tmp_val = netlbl_getinc_u32(&msg_ptr, &msg_len); - entry->type = tmp_val; - switch (tmp_val) { - case NETLBL_NLTYPE_UNLABELED: - ret_val = netlbl_domhsh_add(entry); - break; - case NETLBL_NLTYPE_CIPSOV4: - if (msg_len < NETLBL_LEN_U32) { - ret_val = -EINVAL; - goto add_failure; - } - tmp_val = netlbl_getinc_u32(&msg_ptr, &msg_len); - /* We should be holding a rcu_read_lock() here - * while we hold the result but since the entry - * will always be deleted when the CIPSO DOI - * is deleted we aren't going to keep the lock. */ - rcu_read_lock(); - entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val); - if (entry->type_def.cipsov4 == NULL) { - rcu_read_unlock(); - ret_val = -EINVAL; - goto add_failure; - } - ret_val = netlbl_domhsh_add(entry); - rcu_read_unlock(); - break; - default: - ret_val = -EINVAL; - } - if (ret_val != 0) - goto add_failure; - } - - netlbl_netlink_send_ack(info, - netlbl_mgmt_gnl_family.id, - NLBL_MGMT_C_ACK, - NETLBL_E_OK); return 0; add_failure: if (entry) kfree(entry->domain); kfree(entry); - netlbl_netlink_send_ack(info, - netlbl_mgmt_gnl_family.id, - NLBL_MGMT_C_ACK, - -ret_val); return ret_val; } @@ -176,87 +155,98 @@ add_failure: */ static int netlbl_mgmt_remove(struct sk_buff *skb, struct genl_info *info) { - int ret_val = -EINVAL; - struct nlattr *msg_ptr = netlbl_netlink_payload_data(skb); - int msg_len = netlbl_netlink_payload_len(skb); - u32 count; - u32 iter; - int tmp_size; - unsigned char *domain; + char *domain; - ret_val = netlbl_netlink_cap_check(skb, CAP_NET_ADMIN); + if (!info->attrs[NLBL_MGMT_A_DOMAIN]) + return -EINVAL; + + domain = nla_data(info->attrs[NLBL_MGMT_A_DOMAIN]); + return netlbl_domhsh_remove(domain); +} + +/** + * netlbl_mgmt_listall_cb - netlbl_domhsh_walk() callback for LISTALL + * @entry: the domain mapping hash table entry + * @arg: the netlbl_domhsh_walk_arg structure + * + * Description: + * This function is designed to be used as a callback to the + * netlbl_domhsh_walk() function for use in generating a response for a LISTALL + * message. Returns the size of the message on success, negative values on + * failure. + * + */ +static int netlbl_mgmt_listall_cb(struct netlbl_dom_map *entry, void *arg) +{ + int ret_val = -ENOMEM; + struct netlbl_domhsh_walk_arg *cb_arg = arg; + void *data; + + data = netlbl_netlink_hdr_put(cb_arg->skb, + NETLINK_CB(cb_arg->nl_cb->skb).pid, + cb_arg->seq, + netlbl_mgmt_gnl_family.id, + NLM_F_MULTI, + NLBL_MGMT_C_LISTALL); + if (data == NULL) + goto listall_cb_failure; + + ret_val = nla_put_string(cb_arg->skb, + NLBL_MGMT_A_DOMAIN, + entry->domain); if (ret_val != 0) - goto remove_return; - - if (msg_len < NETLBL_LEN_U32) - goto remove_return; - count = netlbl_getinc_u32(&msg_ptr, &msg_len); - - for (iter = 0; iter < count && msg_len > 0; iter++) { - if (msg_len <= 0) { - ret_val = -EINVAL; - goto remove_return; - } - tmp_size = nla_len(msg_ptr); - domain = nla_data(msg_ptr); - if (tmp_size <= 0 || tmp_size > msg_len || - domain[tmp_size - 1] != '\0') { - ret_val = -EINVAL; - goto remove_return; - } - ret_val = netlbl_domhsh_remove(domain); + goto listall_cb_failure; + ret_val = nla_put_u32(cb_arg->skb, NLBL_MGMT_A_PROTOCOL, entry->type); + if (ret_val != 0) + goto listall_cb_failure; + switch (entry->type) { + case NETLBL_NLTYPE_CIPSOV4: + ret_val = nla_put_u32(cb_arg->skb, + NLBL_MGMT_A_CV4DOI, + entry->type_def.cipsov4->doi); if (ret_val != 0) - goto remove_return; - msg_ptr = nla_next(msg_ptr, &msg_len); + goto listall_cb_failure; + break; } - ret_val = 0; + cb_arg->seq++; + return genlmsg_end(cb_arg->skb, data); -remove_return: - netlbl_netlink_send_ack(info, - netlbl_mgmt_gnl_family.id, - NLBL_MGMT_C_ACK, - -ret_val); +listall_cb_failure: + genlmsg_cancel(cb_arg->skb, data); return ret_val; } /** - * netlbl_mgmt_list - Handle a LIST message + * netlbl_mgmt_listall - Handle a LISTALL message * @skb: the NETLINK buffer - * @info: the Generic NETLINK info block + * @cb: the NETLINK callback * * Description: - * Process a user generated LIST message and dumps the domain hash table in a - * form suitable for use in a kernel generated LIST message. Returns zero on - * success, negative values on failure. + * Process a user generated LISTALL message and dumps the domain hash table in + * a form suitable for use in a kernel generated LISTALL message. Returns zero + * on success, negative values on failure. * */ -static int netlbl_mgmt_list(struct sk_buff *skb, struct genl_info *info) +static int netlbl_mgmt_listall(struct sk_buff *skb, + struct netlink_callback *cb) { - int ret_val = -ENOMEM; - struct sk_buff *ans_skb; + struct netlbl_domhsh_walk_arg cb_arg; + u32 skip_bkt = cb->args[0]; + u32 skip_chain = cb->args[1]; - ans_skb = netlbl_domhsh_dump(NLMSG_SPACE(GENL_HDRLEN)); - if (ans_skb == NULL) - goto list_failure; - netlbl_netlink_hdr_push(ans_skb, - info->snd_pid, - 0, - netlbl_mgmt_gnl_family.id, - NLBL_MGMT_C_LIST); + cb_arg.nl_cb = cb; + cb_arg.skb = skb; + cb_arg.seq = cb->nlh->nlmsg_seq; - ret_val = netlbl_netlink_snd(ans_skb, info->snd_pid); - if (ret_val != 0) - goto list_failure; + netlbl_domhsh_walk(&skip_bkt, + &skip_chain, + netlbl_mgmt_listall_cb, + &cb_arg); - return 0; - -list_failure: - netlbl_netlink_send_ack(info, - netlbl_mgmt_gnl_family.id, - NLBL_MGMT_C_ACK, - -ret_val); - return ret_val; + cb->args[0] = skip_bkt; + cb->args[1] = skip_chain; + return skb->len; } /** @@ -272,68 +262,51 @@ list_failure: static int netlbl_mgmt_adddef(struct sk_buff *skb, struct genl_info *info) { int ret_val = -EINVAL; - struct nlattr *msg_ptr = netlbl_netlink_payload_data(skb); - int msg_len = netlbl_netlink_payload_len(skb); struct netlbl_dom_map *entry = NULL; u32 tmp_val; - ret_val = netlbl_netlink_cap_check(skb, CAP_NET_ADMIN); - if (ret_val != 0) + if (!info->attrs[NLBL_MGMT_A_PROTOCOL]) goto adddef_failure; - if (msg_len < NETLBL_LEN_U32) - goto adddef_failure; - tmp_val = netlbl_getinc_u32(&msg_ptr, &msg_len); - entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (entry == NULL) { ret_val = -ENOMEM; goto adddef_failure; } + entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]); - entry->type = tmp_val; switch (entry->type) { case NETLBL_NLTYPE_UNLABELED: ret_val = netlbl_domhsh_add_default(entry); break; case NETLBL_NLTYPE_CIPSOV4: - if (msg_len < NETLBL_LEN_U32) { - ret_val = -EINVAL; + if (!info->attrs[NLBL_MGMT_A_CV4DOI]) goto adddef_failure; - } - tmp_val = netlbl_getinc_u32(&msg_ptr, &msg_len); - /* We should be holding a rcu_read_lock here while we - * hold the result but since the entry will always be - * deleted when the CIPSO DOI is deleted we are going - * to skip the lock. */ + + tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]); + /* We should be holding a rcu_read_lock() here while we hold + * the result but since the entry will always be deleted when + * the CIPSO DOI is deleted we aren't going to keep the + * lock. */ rcu_read_lock(); entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val); if (entry->type_def.cipsov4 == NULL) { rcu_read_unlock(); - ret_val = -EINVAL; goto adddef_failure; } ret_val = netlbl_domhsh_add_default(entry); rcu_read_unlock(); break; default: - ret_val = -EINVAL; + goto adddef_failure; } if (ret_val != 0) goto adddef_failure; - netlbl_netlink_send_ack(info, - netlbl_mgmt_gnl_family.id, - NLBL_MGMT_C_ACK, - NETLBL_E_OK); return 0; adddef_failure: kfree(entry); - netlbl_netlink_send_ack(info, - netlbl_mgmt_gnl_family.id, - NLBL_MGMT_C_ACK, - -ret_val); return ret_val; } @@ -349,20 +322,7 @@ adddef_failure: */ static int netlbl_mgmt_removedef(struct sk_buff *skb, struct genl_info *info) { - int ret_val; - - ret_val = netlbl_netlink_cap_check(skb, CAP_NET_ADMIN); - if (ret_val != 0) - goto removedef_return; - - ret_val = netlbl_domhsh_remove_default(); - -removedef_return: - netlbl_netlink_send_ack(info, - netlbl_mgmt_gnl_family.id, - NLBL_MGMT_C_ACK, - -ret_val); - return ret_val; + return netlbl_domhsh_remove_default(); } /** @@ -379,87 +339,130 @@ removedef_return: static int netlbl_mgmt_listdef(struct sk_buff *skb, struct genl_info *info) { int ret_val = -ENOMEM; - struct sk_buff *ans_skb; + struct sk_buff *ans_skb = NULL; + void *data; + struct netlbl_dom_map *entry; - ans_skb = netlbl_domhsh_dump_default(NLMSG_SPACE(GENL_HDRLEN)); + ans_skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); if (ans_skb == NULL) + return -ENOMEM; + data = netlbl_netlink_hdr_put(ans_skb, + info->snd_pid, + info->snd_seq, + netlbl_mgmt_gnl_family.id, + 0, + NLBL_MGMT_C_LISTDEF); + if (data == NULL) goto listdef_failure; - netlbl_netlink_hdr_push(ans_skb, - info->snd_pid, - 0, - netlbl_mgmt_gnl_family.id, - NLBL_MGMT_C_LISTDEF); - ret_val = netlbl_netlink_snd(ans_skb, info->snd_pid); + rcu_read_lock(); + entry = netlbl_domhsh_getentry(NULL); + if (entry == NULL) { + ret_val = -ENOENT; + goto listdef_failure_lock; + } + ret_val = nla_put_u32(ans_skb, NLBL_MGMT_A_PROTOCOL, entry->type); + if (ret_val != 0) + goto listdef_failure_lock; + switch (entry->type) { + case NETLBL_NLTYPE_CIPSOV4: + ret_val = nla_put_u32(ans_skb, + NLBL_MGMT_A_CV4DOI, + entry->type_def.cipsov4->doi); + if (ret_val != 0) + goto listdef_failure_lock; + break; + } + rcu_read_unlock(); + + genlmsg_end(ans_skb, data); + + ret_val = genlmsg_unicast(ans_skb, info->snd_pid); if (ret_val != 0) goto listdef_failure; - return 0; +listdef_failure_lock: + rcu_read_unlock(); listdef_failure: - netlbl_netlink_send_ack(info, - netlbl_mgmt_gnl_family.id, - NLBL_MGMT_C_ACK, - -ret_val); + kfree_skb(ans_skb); return ret_val; } /** - * netlbl_mgmt_modules - Handle a MODULES message - * @skb: the NETLINK buffer - * @info: the Generic NETLINK info block + * netlbl_mgmt_protocols_cb - Write an individual PROTOCOL message response + * @skb: the skb to write to + * @seq: the NETLINK sequence number + * @cb: the NETLINK callback + * @protocol: the NetLabel protocol to use in the message * * Description: - * Process a user generated MODULES message and respond accordingly. + * This function is to be used in conjunction with netlbl_mgmt_protocols() to + * answer a application's PROTOCOLS message. Returns the size of the message + * on success, negative values on failure. * */ -static int netlbl_mgmt_modules(struct sk_buff *skb, struct genl_info *info) +static int netlbl_mgmt_protocols_cb(struct sk_buff *skb, + struct netlink_callback *cb, + u32 protocol) { int ret_val = -ENOMEM; - size_t data_size; - u32 mod_count; - struct sk_buff *ans_skb = NULL; + void *data; - /* unlabeled + cipsov4 */ - mod_count = 2; + data = netlbl_netlink_hdr_put(skb, + NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, + netlbl_mgmt_gnl_family.id, + NLM_F_MULTI, + NLBL_MGMT_C_PROTOCOLS); + if (data == NULL) + goto protocols_cb_failure; - data_size = GENL_HDRLEN + NETLBL_LEN_U32 + mod_count * NETLBL_LEN_U32; - ans_skb = netlbl_netlink_alloc_skb(0, data_size, GFP_KERNEL); - if (ans_skb == NULL) - goto modules_failure; - - if (netlbl_netlink_hdr_put(ans_skb, - info->snd_pid, - 0, - netlbl_mgmt_gnl_family.id, - NLBL_MGMT_C_MODULES) == NULL) - goto modules_failure; - - ret_val = nla_put_u32(ans_skb, NLA_U32, mod_count); + ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, protocol); if (ret_val != 0) - goto modules_failure; - ret_val = nla_put_u32(ans_skb, NLA_U32, NETLBL_NLTYPE_UNLABELED); - if (ret_val != 0) - goto modules_failure; - ret_val = nla_put_u32(ans_skb, NLA_U32, NETLBL_NLTYPE_CIPSOV4); - if (ret_val != 0) - goto modules_failure; + goto protocols_cb_failure; - ret_val = netlbl_netlink_snd(ans_skb, info->snd_pid); - if (ret_val != 0) - goto modules_failure; + return genlmsg_end(skb, data); - return 0; - -modules_failure: - kfree_skb(ans_skb); - netlbl_netlink_send_ack(info, - netlbl_mgmt_gnl_family.id, - NLBL_MGMT_C_ACK, - -ret_val); +protocols_cb_failure: + genlmsg_cancel(skb, data); return ret_val; } +/** + * netlbl_mgmt_protocols - Handle a PROTOCOLS message + * @skb: the NETLINK buffer + * @cb: the NETLINK callback + * + * Description: + * Process a user generated PROTOCOLS message and respond accordingly. + * + */ +static int netlbl_mgmt_protocols(struct sk_buff *skb, + struct netlink_callback *cb) +{ + u32 protos_sent = cb->args[0]; + + if (protos_sent == 0) { + if (netlbl_mgmt_protocols_cb(skb, + cb, + NETLBL_NLTYPE_UNLABELED) < 0) + goto protocols_return; + protos_sent++; + } + if (protos_sent == 1) { + if (netlbl_mgmt_protocols_cb(skb, + cb, + NETLBL_NLTYPE_CIPSOV4) < 0) + goto protocols_return; + protos_sent++; + } + +protocols_return: + cb->args[0] = protos_sent; + return skb->len; +} + /** * netlbl_mgmt_version - Handle a VERSION message * @skb: the NETLINK buffer @@ -474,35 +477,35 @@ static int netlbl_mgmt_version(struct sk_buff *skb, struct genl_info *info) { int ret_val = -ENOMEM; struct sk_buff *ans_skb = NULL; + void *data; - ans_skb = netlbl_netlink_alloc_skb(0, - GENL_HDRLEN + NETLBL_LEN_U32, - GFP_KERNEL); + ans_skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); if (ans_skb == NULL) - goto version_failure; - if (netlbl_netlink_hdr_put(ans_skb, - info->snd_pid, - 0, - netlbl_mgmt_gnl_family.id, - NLBL_MGMT_C_VERSION) == NULL) + return -ENOMEM; + data = netlbl_netlink_hdr_put(ans_skb, + info->snd_pid, + info->snd_seq, + netlbl_mgmt_gnl_family.id, + 0, + NLBL_MGMT_C_VERSION); + if (data == NULL) goto version_failure; - ret_val = nla_put_u32(ans_skb, NLA_U32, NETLBL_PROTO_VERSION); + ret_val = nla_put_u32(ans_skb, + NLBL_MGMT_A_VERSION, + NETLBL_PROTO_VERSION); if (ret_val != 0) goto version_failure; - ret_val = netlbl_netlink_snd(ans_skb, info->snd_pid); + genlmsg_end(ans_skb, data); + + ret_val = genlmsg_unicast(ans_skb, info->snd_pid); if (ret_val != 0) goto version_failure; - return 0; version_failure: kfree_skb(ans_skb); - netlbl_netlink_send_ack(info, - netlbl_mgmt_gnl_family.id, - NLBL_MGMT_C_ACK, - -ret_val); return ret_val; } @@ -513,35 +516,40 @@ version_failure: static struct genl_ops netlbl_mgmt_genl_c_add = { .cmd = NLBL_MGMT_C_ADD, - .flags = 0, + .flags = GENL_ADMIN_PERM, + .policy = netlbl_mgmt_genl_policy, .doit = netlbl_mgmt_add, .dumpit = NULL, }; static struct genl_ops netlbl_mgmt_genl_c_remove = { .cmd = NLBL_MGMT_C_REMOVE, - .flags = 0, + .flags = GENL_ADMIN_PERM, + .policy = netlbl_mgmt_genl_policy, .doit = netlbl_mgmt_remove, .dumpit = NULL, }; -static struct genl_ops netlbl_mgmt_genl_c_list = { - .cmd = NLBL_MGMT_C_LIST, +static struct genl_ops netlbl_mgmt_genl_c_listall = { + .cmd = NLBL_MGMT_C_LISTALL, .flags = 0, - .doit = netlbl_mgmt_list, - .dumpit = NULL, + .policy = netlbl_mgmt_genl_policy, + .doit = NULL, + .dumpit = netlbl_mgmt_listall, }; static struct genl_ops netlbl_mgmt_genl_c_adddef = { .cmd = NLBL_MGMT_C_ADDDEF, - .flags = 0, + .flags = GENL_ADMIN_PERM, + .policy = netlbl_mgmt_genl_policy, .doit = netlbl_mgmt_adddef, .dumpit = NULL, }; static struct genl_ops netlbl_mgmt_genl_c_removedef = { .cmd = NLBL_MGMT_C_REMOVEDEF, - .flags = 0, + .flags = GENL_ADMIN_PERM, + .policy = netlbl_mgmt_genl_policy, .doit = netlbl_mgmt_removedef, .dumpit = NULL, }; @@ -549,20 +557,23 @@ static struct genl_ops netlbl_mgmt_genl_c_removedef = { static struct genl_ops netlbl_mgmt_genl_c_listdef = { .cmd = NLBL_MGMT_C_LISTDEF, .flags = 0, + .policy = netlbl_mgmt_genl_policy, .doit = netlbl_mgmt_listdef, .dumpit = NULL, }; -static struct genl_ops netlbl_mgmt_genl_c_modules = { - .cmd = NLBL_MGMT_C_MODULES, +static struct genl_ops netlbl_mgmt_genl_c_protocols = { + .cmd = NLBL_MGMT_C_PROTOCOLS, .flags = 0, - .doit = netlbl_mgmt_modules, - .dumpit = NULL, + .policy = netlbl_mgmt_genl_policy, + .doit = NULL, + .dumpit = netlbl_mgmt_protocols, }; static struct genl_ops netlbl_mgmt_genl_c_version = { .cmd = NLBL_MGMT_C_VERSION, .flags = 0, + .policy = netlbl_mgmt_genl_policy, .doit = netlbl_mgmt_version, .dumpit = NULL, }; @@ -596,7 +607,7 @@ int netlbl_mgmt_genl_init(void) if (ret_val != 0) return ret_val; ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, - &netlbl_mgmt_genl_c_list); + &netlbl_mgmt_genl_c_listall); if (ret_val != 0) return ret_val; ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, @@ -612,7 +623,7 @@ int netlbl_mgmt_genl_init(void) if (ret_val != 0) return ret_val; ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, - &netlbl_mgmt_genl_c_modules); + &netlbl_mgmt_genl_c_protocols); if (ret_val != 0) return ret_val; ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, diff --git a/net/netlabel/netlabel_mgmt.h b/net/netlabel/netlabel_mgmt.h index fd6c6acbfa08..3642d3bfc8eb 100644 --- a/net/netlabel/netlabel_mgmt.h +++ b/net/netlabel/netlabel_mgmt.h @@ -34,212 +34,137 @@ #include /* - * The following NetLabel payloads are supported by the management interface, - * all of which are preceeded by the nlmsghdr struct. - * - * o ACK: - * Sent by the kernel in response to an applications message, applications - * should never send this message. - * - * +----------------------+-----------------------+ - * | seq number (32 bits) | return code (32 bits) | - * +----------------------+-----------------------+ - * - * seq number: the sequence number of the original message, taken from the - * nlmsghdr structure - * return code: return value, based on errno values + * The following NetLabel payloads are supported by the management interface. * * o ADD: * Sent by an application to add a domain mapping to the NetLabel system. - * The kernel should respond with an ACK. * - * +-------------------+ - * | domains (32 bits) | ... - * +-------------------+ + * Required attributes: * - * domains: the number of domains in the message + * NLBL_MGMT_A_DOMAIN + * NLBL_MGMT_A_PROTOCOL * - * +--------------------------+-------------------------+ - * | domain string (variable) | protocol type (32 bits) | ... - * +--------------------------+-------------------------+ + * If using NETLBL_NLTYPE_CIPSOV4 the following attributes are required: * - * +-------------- ---- --- -- - - * | mapping data ... repeated - * +-------------- ---- --- -- - + * NLBL_MGMT_A_CV4DOI * - * domain string: the domain string, NULL terminated - * protocol type: the protocol type (defined by NETLBL_NLTYPE_*) - * mapping data: specific to the map type (see below) - * - * NETLBL_NLTYPE_UNLABELED - * - * No mapping data for this protocol type. - * - * NETLBL_NLTYPE_CIPSOV4 - * - * +---------------+ - * | doi (32 bits) | - * +---------------+ - * - * doi: the CIPSO DOI value + * If using NETLBL_NLTYPE_UNLABELED no other attributes are required. * * o REMOVE: * Sent by an application to remove a domain mapping from the NetLabel - * system. The kernel should ACK this message. + * system. * - * +-------------------+ - * | domains (32 bits) | ... - * +-------------------+ + * Required attributes: * - * domains: the number of domains in the message + * NLBL_MGMT_A_DOMAIN * - * +--------------------------+ - * | domain string (variable) | ... - * +--------------------------+ - * - * domain string: the domain string, NULL terminated - * - * o LIST: + * o LISTALL: * This message can be sent either from an application or by the kernel in - * response to an application generated LIST message. When sent by an - * application there is no payload. The kernel should respond to a LIST - * message either with a LIST message on success or an ACK message on - * failure. + * response to an application generated LISTALL message. When sent by an + * application there is no payload and the NLM_F_DUMP flag should be set. + * The kernel should respond with a series of the following messages. * - * +-------------------+ - * | domains (32 bits) | ... - * +-------------------+ + * Required attributes: * - * domains: the number of domains in the message + * NLBL_MGMT_A_DOMAIN + * NLBL_MGMT_A_PROTOCOL * - * +--------------------------+ - * | domain string (variable) | ... - * +--------------------------+ + * If using NETLBL_NLTYPE_CIPSOV4 the following attributes are required: * - * +-------------------------+-------------- ---- --- -- - - * | protocol type (32 bits) | mapping data ... repeated - * +-------------------------+-------------- ---- --- -- - + * NLBL_MGMT_A_CV4DOI * - * domain string: the domain string, NULL terminated - * protocol type: the protocol type (defined by NETLBL_NLTYPE_*) - * mapping data: specific to the map type (see below) - * - * NETLBL_NLTYPE_UNLABELED - * - * No mapping data for this protocol type. - * - * NETLBL_NLTYPE_CIPSOV4 - * - * +----------------+---------------+ - * | type (32 bits) | doi (32 bits) | - * +----------------+---------------+ - * - * type: the CIPSO mapping table type (defined in the cipso_ipv4.h header - * as CIPSO_V4_MAP_*) - * doi: the CIPSO DOI value + * If using NETLBL_NLTYPE_UNLABELED no other attributes are required. * * o ADDDEF: * Sent by an application to set the default domain mapping for the NetLabel - * system. The kernel should respond with an ACK. + * system. * - * +-------------------------+-------------- ---- --- -- - - * | protocol type (32 bits) | mapping data ... repeated - * +-------------------------+-------------- ---- --- -- - + * Required attributes: * - * protocol type: the protocol type (defined by NETLBL_NLTYPE_*) - * mapping data: specific to the map type (see below) + * NLBL_MGMT_A_PROTOCOL * - * NETLBL_NLTYPE_UNLABELED + * If using NETLBL_NLTYPE_CIPSOV4 the following attributes are required: * - * No mapping data for this protocol type. + * NLBL_MGMT_A_CV4DOI * - * NETLBL_NLTYPE_CIPSOV4 - * - * +---------------+ - * | doi (32 bits) | - * +---------------+ - * - * doi: the CIPSO DOI value + * If using NETLBL_NLTYPE_UNLABELED no other attributes are required. * * o REMOVEDEF: * Sent by an application to remove the default domain mapping from the - * NetLabel system, there is no payload. The kernel should ACK this message. + * NetLabel system, there is no payload. * * o LISTDEF: * This message can be sent either from an application or by the kernel in * response to an application generated LISTDEF message. When sent by an - * application there is no payload. The kernel should respond to a - * LISTDEF message either with a LISTDEF message on success or an ACK message - * on failure. + * application there is no payload. On success the kernel should send a + * response using the following format. * - * +-------------------------+-------------- ---- --- -- - - * | protocol type (32 bits) | mapping data ... repeated - * +-------------------------+-------------- ---- --- -- - + * Required attributes: * - * protocol type: the protocol type (defined by NETLBL_NLTYPE_*) - * mapping data: specific to the map type (see below) + * NLBL_MGMT_A_PROTOCOL * - * NETLBL_NLTYPE_UNLABELED + * If using NETLBL_NLTYPE_CIPSOV4 the following attributes are required: * - * No mapping data for this protocol type. + * NLBL_MGMT_A_CV4DOI * - * NETLBL_NLTYPE_CIPSOV4 + * If using NETLBL_NLTYPE_UNLABELED no other attributes are required. * - * +----------------+---------------+ - * | type (32 bits) | doi (32 bits) | - * +----------------+---------------+ + * o PROTOCOLS: + * Sent by an application to request a list of configured NetLabel protocols + * in the kernel. When sent by an application there is no payload and the + * NLM_F_DUMP flag should be set. The kernel should respond with a series of + * the following messages. * - * type: the CIPSO mapping table type (defined in the cipso_ipv4.h header - * as CIPSO_V4_MAP_*) - * doi: the CIPSO DOI value + * Required attributes: * - * o MODULES: - * Sent by an application to request a list of configured NetLabel modules - * in the kernel. When sent by an application there is no payload. - * - * +-------------------+ - * | modules (32 bits) | ... - * +-------------------+ - * - * modules: the number of modules in the message, if this is an application - * generated message and the value is zero then return a list of - * the configured modules - * - * +------------------+ - * | module (32 bits) | ... repeated - * +------------------+ - * - * module: the module number as defined by NETLBL_NLTYPE_* + * NLBL_MGMT_A_PROTOCOL * * o VERSION: - * Sent by an application to request the NetLabel version string. When sent - * by an application there is no payload. This message type is also used by - * the kernel to respond to an VERSION request. + * Sent by an application to request the NetLabel version. When sent by an + * application there is no payload. This message type is also used by the + * kernel to respond to an VERSION request. * - * +-------------------+ - * | version (32 bits) | - * +-------------------+ + * Required attributes: * - * version: the protocol version number + * NLBL_MGMT_A_VERSION * */ /* NetLabel Management commands */ enum { NLBL_MGMT_C_UNSPEC, - NLBL_MGMT_C_ACK, NLBL_MGMT_C_ADD, NLBL_MGMT_C_REMOVE, - NLBL_MGMT_C_LIST, + NLBL_MGMT_C_LISTALL, NLBL_MGMT_C_ADDDEF, NLBL_MGMT_C_REMOVEDEF, NLBL_MGMT_C_LISTDEF, - NLBL_MGMT_C_MODULES, + NLBL_MGMT_C_PROTOCOLS, NLBL_MGMT_C_VERSION, __NLBL_MGMT_C_MAX, }; #define NLBL_MGMT_C_MAX (__NLBL_MGMT_C_MAX - 1) +/* NetLabel Management attributes */ +enum { + NLBL_MGMT_A_UNSPEC, + NLBL_MGMT_A_DOMAIN, + /* (NLA_NUL_STRING) + * the NULL terminated LSM domain string */ + NLBL_MGMT_A_PROTOCOL, + /* (NLA_U32) + * the NetLabel protocol type (defined by NETLBL_NLTYPE_*) */ + NLBL_MGMT_A_VERSION, + /* (NLA_U32) + * the NetLabel protocol version number (defined by + * NETLBL_PROTO_VERSION) */ + NLBL_MGMT_A_CV4DOI, + /* (NLA_U32) + * the CIPSOv4 DOI value */ + __NLBL_MGMT_A_MAX, +}; +#define NLBL_MGMT_A_MAX (__NLBL_MGMT_A_MAX - 1) + /* NetLabel protocol functions */ int netlbl_mgmt_genl_init(void); diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index 785f4960e0d3..440f5c4e1e2d 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -55,9 +55,13 @@ static struct genl_family netlbl_unlabel_gnl_family = { .hdrsize = 0, .name = NETLBL_NLTYPE_UNLABELED_NAME, .version = NETLBL_PROTO_VERSION, - .maxattr = 0, + .maxattr = NLBL_UNLABEL_A_MAX, }; +/* NetLabel Netlink attribute policy */ +static struct nla_policy netlbl_unlabel_genl_policy[NLBL_UNLABEL_A_MAX + 1] = { + [NLBL_UNLABEL_A_ACPTFLG] = { .type = NLA_U8 }, +}; /* * NetLabel Command Handlers @@ -75,31 +79,18 @@ static struct genl_family netlbl_unlabel_gnl_family = { */ static int netlbl_unlabel_accept(struct sk_buff *skb, struct genl_info *info) { - int ret_val; - struct nlattr *data = netlbl_netlink_payload_data(skb); - u32 value; + int ret_val = -EINVAL; + u8 value; - ret_val = netlbl_netlink_cap_check(skb, CAP_NET_ADMIN); - if (ret_val != 0) - return ret_val; - - if (netlbl_netlink_payload_len(skb) == NETLBL_LEN_U32) { - value = nla_get_u32(data); + if (info->attrs[NLBL_UNLABEL_A_ACPTFLG]) { + value = nla_get_u8(info->attrs[NLBL_UNLABEL_A_ACPTFLG]); if (value == 1 || value == 0) { atomic_set(&netlabel_unlabel_accept_flg, value); - netlbl_netlink_send_ack(info, - netlbl_unlabel_gnl_family.id, - NLBL_UNLABEL_C_ACK, - NETLBL_E_OK); - return 0; + ret_val = 0; } } - netlbl_netlink_send_ack(info, - netlbl_unlabel_gnl_family.id, - NLBL_UNLABEL_C_ACK, - EINVAL); - return -EINVAL; + return ret_val; } /** @@ -114,39 +105,39 @@ static int netlbl_unlabel_accept(struct sk_buff *skb, struct genl_info *info) */ static int netlbl_unlabel_list(struct sk_buff *skb, struct genl_info *info) { - int ret_val = -ENOMEM; + int ret_val = -EINVAL; struct sk_buff *ans_skb; + void *data; - ans_skb = netlbl_netlink_alloc_skb(0, - GENL_HDRLEN + NETLBL_LEN_U32, - GFP_KERNEL); + ans_skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); if (ans_skb == NULL) goto list_failure; - - if (netlbl_netlink_hdr_put(ans_skb, - info->snd_pid, - 0, - netlbl_unlabel_gnl_family.id, - NLBL_UNLABEL_C_LIST) == NULL) + data = netlbl_netlink_hdr_put(ans_skb, + info->snd_pid, + info->snd_seq, + netlbl_unlabel_gnl_family.id, + 0, + NLBL_UNLABEL_C_LIST); + if (data == NULL) { + ret_val = -ENOMEM; goto list_failure; + } - ret_val = nla_put_u32(ans_skb, - NLA_U32, - atomic_read(&netlabel_unlabel_accept_flg)); + ret_val = nla_put_u8(ans_skb, + NLBL_UNLABEL_A_ACPTFLG, + atomic_read(&netlabel_unlabel_accept_flg)); if (ret_val != 0) goto list_failure; - ret_val = netlbl_netlink_snd(ans_skb, info->snd_pid); + genlmsg_end(ans_skb, data); + + ret_val = genlmsg_unicast(ans_skb, info->snd_pid); if (ret_val != 0) goto list_failure; - return 0; list_failure: - netlbl_netlink_send_ack(info, - netlbl_unlabel_gnl_family.id, - NLBL_UNLABEL_C_ACK, - -ret_val); + kfree(ans_skb); return ret_val; } @@ -157,7 +148,8 @@ list_failure: static struct genl_ops netlbl_unlabel_genl_c_accept = { .cmd = NLBL_UNLABEL_C_ACCEPT, - .flags = 0, + .flags = GENL_ADMIN_PERM, + .policy = netlbl_unlabel_genl_policy, .doit = netlbl_unlabel_accept, .dumpit = NULL, }; @@ -165,6 +157,7 @@ static struct genl_ops netlbl_unlabel_genl_c_accept = { static struct genl_ops netlbl_unlabel_genl_c_list = { .cmd = NLBL_UNLABEL_C_LIST, .flags = 0, + .policy = netlbl_unlabel_genl_policy, .doit = netlbl_unlabel_list, .dumpit = NULL, }; @@ -218,10 +211,8 @@ int netlbl_unlabel_genl_init(void) */ int netlbl_unlabel_getattr(struct netlbl_lsm_secattr *secattr) { - if (atomic_read(&netlabel_unlabel_accept_flg) == 1) { - memset(secattr, 0, sizeof(*secattr)); - return 0; - } + if (atomic_read(&netlabel_unlabel_accept_flg) == 1) + return netlbl_secattr_init(secattr); return -ENOMSG; } diff --git a/net/netlabel/netlabel_unlabeled.h b/net/netlabel/netlabel_unlabeled.h index f300e54e14b6..c2917fbb42cf 100644 --- a/net/netlabel/netlabel_unlabeled.h +++ b/net/netlabel/netlabel_unlabeled.h @@ -36,56 +36,47 @@ /* * The following NetLabel payloads are supported by the Unlabeled subsystem. * - * o ACK: - * Sent by the kernel in response to an applications message, applications - * should never send this message. - * - * +----------------------+-----------------------+ - * | seq number (32 bits) | return code (32 bits) | - * +----------------------+-----------------------+ - * - * seq number: the sequence number of the original message, taken from the - * nlmsghdr structure - * return code: return value, based on errno values - * * o ACCEPT * This message is sent from an application to specify if the kernel should * allow unlabled packets to pass if they do not match any of the static * mappings defined in the unlabeled module. * - * +-----------------+ - * | allow (32 bits) | - * +-----------------+ + * Required attributes: * - * allow: if true (1) then allow the packets to pass, if false (0) then - * reject the packets + * NLBL_UNLABEL_A_ACPTFLG * * o LIST * This message can be sent either from an application or by the kernel in * response to an application generated LIST message. When sent by an * application there is no payload. The kernel should respond to a LIST - * message either with a LIST message on success or an ACK message on - * failure. + * message with a LIST message on success. * - * +-----------------------+ - * | accept flag (32 bits) | - * +-----------------------+ + * Required attributes: * - * accept flag: if true (1) then unlabeled packets are allowed to pass, - * if false (0) then unlabeled packets are rejected + * NLBL_UNLABEL_A_ACPTFLG * */ /* NetLabel Unlabeled commands */ enum { NLBL_UNLABEL_C_UNSPEC, - NLBL_UNLABEL_C_ACK, NLBL_UNLABEL_C_ACCEPT, NLBL_UNLABEL_C_LIST, __NLBL_UNLABEL_C_MAX, }; #define NLBL_UNLABEL_C_MAX (__NLBL_UNLABEL_C_MAX - 1) +/* NetLabel Unlabeled attributes */ +enum { + NLBL_UNLABEL_A_UNSPEC, + NLBL_UNLABEL_A_ACPTFLG, + /* (NLA_U8) + * if true then unlabeled packets are allowed to pass, else unlabeled + * packets are rejected */ + __NLBL_UNLABEL_A_MAX, +}; +#define NLBL_UNLABEL_A_MAX (__NLBL_UNLABEL_A_MAX - 1) + /* NetLabel protocol functions */ int netlbl_unlabel_genl_init(void); diff --git a/net/netlabel/netlabel_user.c b/net/netlabel/netlabel_user.c index 73cbe66e42ff..eeb7d768d2bb 100644 --- a/net/netlabel/netlabel_user.c +++ b/net/netlabel/netlabel_user.c @@ -74,85 +74,3 @@ int netlbl_netlink_init(void) return 0; } - -/* - * NetLabel Common Protocol Functions - */ - -/** - * netlbl_netlink_send_ack - Send an ACK message - * @info: the generic NETLINK information - * @genl_family: the generic NETLINK family ID value - * @ack_cmd: the generic NETLINK family ACK command value - * @ret_code: return code to use - * - * Description: - * This function sends an ACK message to the sender of the NETLINK message - * specified by @info. - * - */ -void netlbl_netlink_send_ack(const struct genl_info *info, - u32 genl_family, - u8 ack_cmd, - u32 ret_code) -{ - size_t data_size; - struct sk_buff *skb; - - data_size = GENL_HDRLEN + 2 * NETLBL_LEN_U32; - skb = netlbl_netlink_alloc_skb(0, data_size, GFP_KERNEL); - if (skb == NULL) - return; - - if (netlbl_netlink_hdr_put(skb, - info->snd_pid, - 0, - genl_family, - ack_cmd) == NULL) - goto send_ack_failure; - - if (nla_put_u32(skb, NLA_U32, info->snd_seq) != 0) - goto send_ack_failure; - if (nla_put_u32(skb, NLA_U32, ret_code) != 0) - goto send_ack_failure; - - netlbl_netlink_snd(skb, info->snd_pid); - return; - -send_ack_failure: - kfree_skb(skb); -} - -/* - * NETLINK I/O Functions - */ - -/** - * netlbl_netlink_snd - Send a NetLabel message - * @skb: NetLabel message - * @pid: destination PID - * - * Description: - * Sends a unicast NetLabel message over the NETLINK socket. - * - */ -int netlbl_netlink_snd(struct sk_buff *skb, u32 pid) -{ - return genlmsg_unicast(skb, pid); -} - -/** - * netlbl_netlink_snd - Send a NetLabel message - * @skb: NetLabel message - * @pid: sending PID - * @group: multicast group id - * - * Description: - * Sends a multicast NetLabel message over the NETLINK socket to all members - * of @group except @pid. - * - */ -int netlbl_netlink_snd_multicast(struct sk_buff *skb, u32 pid, u32 group) -{ - return genlmsg_multicast(skb, pid, group, GFP_KERNEL); -} diff --git a/net/netlabel/netlabel_user.h b/net/netlabel/netlabel_user.h index 385a6c7488c6..3f9386b917df 100644 --- a/net/netlabel/netlabel_user.h +++ b/net/netlabel/netlabel_user.h @@ -40,72 +40,6 @@ /* NetLabel NETLINK helper functions */ -/** - * netlbl_netlink_cap_check - Check the NETLINK msg capabilities - * @skb: the NETLINK buffer - * @req_cap: the required capability - * - * Description: - * Check the NETLINK buffer's capabilities against the required capabilities. - * Returns zero on success, negative values on failure. - * - */ -static inline int netlbl_netlink_cap_check(const struct sk_buff *skb, - kernel_cap_t req_cap) -{ - if (cap_raised(NETLINK_CB(skb).eff_cap, req_cap)) - return 0; - return -EPERM; -} - -/** - * netlbl_getinc_u8 - Read a u8 value from a nlattr stream and move on - * @nla: the attribute - * @rem_len: remaining length - * - * Description: - * Return a u8 value pointed to by @nla and advance it to the next attribute. - * - */ -static inline u8 netlbl_getinc_u8(struct nlattr **nla, int *rem_len) -{ - u8 val = nla_get_u8(*nla); - *nla = nla_next(*nla, rem_len); - return val; -} - -/** - * netlbl_getinc_u16 - Read a u16 value from a nlattr stream and move on - * @nla: the attribute - * @rem_len: remaining length - * - * Description: - * Return a u16 value pointed to by @nla and advance it to the next attribute. - * - */ -static inline u16 netlbl_getinc_u16(struct nlattr **nla, int *rem_len) -{ - u16 val = nla_get_u16(*nla); - *nla = nla_next(*nla, rem_len); - return val; -} - -/** - * netlbl_getinc_u32 - Read a u32 value from a nlattr stream and move on - * @nla: the attribute - * @rem_len: remaining length - * - * Description: - * Return a u32 value pointed to by @nla and advance it to the next attribute. - * - */ -static inline u32 netlbl_getinc_u32(struct nlattr **nla, int *rem_len) -{ - u32 val = nla_get_u32(*nla); - *nla = nla_next(*nla, rem_len); - return val; -} - /** * netlbl_netlink_hdr_put - Write the NETLINK buffers into a sk_buff * @skb: the packet @@ -124,6 +58,7 @@ static inline void *netlbl_netlink_hdr_put(struct sk_buff *skb, u32 pid, u32 seq, int type, + int flags, u8 cmd) { return genlmsg_put(skb, @@ -131,85 +66,13 @@ static inline void *netlbl_netlink_hdr_put(struct sk_buff *skb, seq, type, 0, - 0, + flags, cmd, NETLBL_PROTO_VERSION); } -/** - * netlbl_netlink_hdr_push - Write the NETLINK buffers into a sk_buff - * @skb: the packet - * @pid: the PID of the receipient - * @seq: the sequence number - * @type: the generic NETLINK message family type - * @cmd: command - * - * Description: - * Write both a NETLINK nlmsghdr structure and a Generic NETLINK genlmsghdr - * struct to the packet. - * - */ -static inline void netlbl_netlink_hdr_push(struct sk_buff *skb, - u32 pid, - u32 seq, - int type, - u8 cmd) - -{ - struct nlmsghdr *nlh; - struct genlmsghdr *hdr; - - nlh = (struct nlmsghdr *)skb_push(skb, NLMSG_SPACE(GENL_HDRLEN)); - nlh->nlmsg_type = type; - nlh->nlmsg_len = skb->len; - nlh->nlmsg_flags = 0; - nlh->nlmsg_pid = pid; - nlh->nlmsg_seq = seq; - - hdr = nlmsg_data(nlh); - hdr->cmd = cmd; - hdr->version = NETLBL_PROTO_VERSION; - hdr->reserved = 0; -} - -/** - * netlbl_netlink_payload_len - Return the length of the payload - * @skb: the NETLINK buffer - * - * Description: - * This function returns the length of the NetLabel payload. - * - */ -static inline u32 netlbl_netlink_payload_len(const struct sk_buff *skb) -{ - return nlmsg_len((struct nlmsghdr *)skb->data) - GENL_HDRLEN; -} - -/** - * netlbl_netlink_payload_data - Returns a pointer to the start of the payload - * @skb: the NETLINK buffer - * - * Description: - * This function returns a pointer to the start of the NetLabel payload. - * - */ -static inline void *netlbl_netlink_payload_data(const struct sk_buff *skb) -{ - return (unsigned char *)nlmsg_data((struct nlmsghdr *)skb->data) + - GENL_HDRLEN; -} - -/* NetLabel common protocol functions */ - -void netlbl_netlink_send_ack(const struct genl_info *info, - u32 genl_family, - u8 ack_cmd, - u32 ret_code); - /* NetLabel NETLINK I/O functions */ int netlbl_netlink_init(void); -int netlbl_netlink_snd(struct sk_buff *skb, u32 pid); -int netlbl_netlink_snd_multicast(struct sk_buff *skb, u32 pid, u32 group); #endif diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 7eb69a602d8f..22ed17c17718 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2502,14 +2502,24 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) { struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; struct sk_security_struct *sksec = sk->sk_security; + struct netlbl_lsm_secattr secattr; + u32 nlbl_peer_sid; sksec->sclass = isec->sclass; if (sk->sk_family != PF_INET) return; + netlbl_secattr_init(&secattr); + if (netlbl_sock_getattr(sk, &secattr) == 0 && + selinux_netlbl_secattr_to_sid(NULL, + &secattr, + sksec->sid, + &nlbl_peer_sid) == 0) + sksec->peer_sid = nlbl_peer_sid; + netlbl_secattr_destroy(&secattr, 0); + sksec->nlbl_state = NLBL_REQUIRE; - sksec->peer_sid = sksec->sid; /* Try to set the NetLabel on the socket to save time later, if we fail * here we will pick up the pieces in later calls to @@ -2601,7 +2611,7 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, u32 netlbl_sid; u32 recv_perm; - rc = selinux_netlbl_skbuff_getsid(skb, sksec->sid, &netlbl_sid); + rc = selinux_netlbl_skbuff_getsid(skb, SECINITSID_NETMSG, &netlbl_sid); if (rc != 0) return rc; @@ -2610,13 +2620,13 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, switch (sksec->sclass) { case SECCLASS_UDP_SOCKET: - recv_perm = UDP_SOCKET__RECV_MSG; + recv_perm = UDP_SOCKET__RECVFROM; break; case SECCLASS_TCP_SOCKET: - recv_perm = TCP_SOCKET__RECV_MSG; + recv_perm = TCP_SOCKET__RECVFROM; break; default: - recv_perm = RAWIP_SOCKET__RECV_MSG; + recv_perm = RAWIP_SOCKET__RECVFROM; } rc = avc_has_perm(sksec->sid,