X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=net%2Fipv4%2Finet_diag.c;h=b0170732b5e9e962ed1fca65cf930c3bf4cc98f0;hb=c5b875e354a54e2b5ba24eecae69bf94e025edd5;hp=492858e6faf0140fee625d8f966093c57c141773;hpb=20f99dcf417a28089ef6c877ae97f5dec2eab435;p=linux-2.6 diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 492858e6fa..b0170732b5 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -11,6 +11,7 @@ * 2 of the License, or (at your option) any later version. */ +#include #include #include #include @@ -27,6 +28,7 @@ #include #include #include +#include #include #include @@ -36,8 +38,8 @@ static const struct inet_diag_handler **inet_diag_table; struct inet_diag_entry { - u32 *saddr; - u32 *daddr; + __be32 *saddr; + __be32 *daddr; u16 sport; u16 dport; u16 family; @@ -60,7 +62,7 @@ static int inet_csk_diag_fill(struct sock *sk, struct nlmsghdr *nlh; void *info = NULL; struct inet_diag_meminfo *minfo = NULL; - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); const struct inet_diag_handler *handler; handler = inet_diag_table[unlh->nlmsg_type]; @@ -111,7 +113,7 @@ static int inet_csk_diag_fill(struct sock *sk, } #endif -#define EXPIRES_IN_MS(tmo) ((tmo - jiffies) * 1000 + HZ - 1) / HZ +#define EXPIRES_IN_MS(tmo) DIV_ROUND_UP((tmo - jiffies) * 1000, HZ) if (icsk->icsk_pending == ICSK_TIME_RETRANS) { r->idiag_timer = 1; @@ -147,13 +149,13 @@ static int inet_csk_diag_fill(struct sock *sk, icsk->icsk_ca_ops && icsk->icsk_ca_ops->get_info) icsk->icsk_ca_ops->get_info(sk, ext, skb); - nlh->nlmsg_len = skb->tail - b; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; rtattr_failure: nlmsg_failure: - skb_trim(skb, b - skb->data); - return -1; + nlmsg_trim(skb, b); + return -EMSGSIZE; } static int inet_twsk_diag_fill(struct inet_timewait_sock *tw, @@ -163,7 +165,7 @@ static int inet_twsk_diag_fill(struct inet_timewait_sock *tw, { long tmo; struct inet_diag_msg *r; - const unsigned char *previous_tail = skb->tail; + const unsigned char *previous_tail = skb_tail_pointer(skb); struct nlmsghdr *nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r)); @@ -189,7 +191,7 @@ static int inet_twsk_diag_fill(struct inet_timewait_sock *tw, r->id.idiag_dst[0] = tw->tw_daddr; r->idiag_state = tw->tw_substate; r->idiag_timer = 3; - r->idiag_expires = (tmo * 1000 + HZ - 1) / HZ; + r->idiag_expires = DIV_ROUND_UP(tmo * 1000, HZ); r->idiag_rqueue = 0; r->idiag_wqueue = 0; r->idiag_uid = 0; @@ -205,11 +207,11 @@ static int inet_twsk_diag_fill(struct inet_timewait_sock *tw, &tw6->tw_v6_daddr); } #endif - nlh->nlmsg_len = skb->tail - previous_tail; + nlh->nlmsg_len = skb_tail_pointer(skb) - previous_tail; return skb->len; nlmsg_failure: - skb_trim(skb, previous_tail - skb->data); - return -1; + nlmsg_trim(skb, previous_tail); + return -EMSGSIZE; } static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, @@ -274,11 +276,14 @@ static int inet_diag_get_exact(struct sk_buff *in_skb, if (!rep) goto out; - if (sk_diag_fill(sk, rep, req->idiag_ext, - NETLINK_CB(in_skb).pid, - nlh->nlmsg_seq, 0, nlh) <= 0) - BUG(); - + err = sk_diag_fill(sk, rep, req->idiag_ext, + NETLINK_CB(in_skb).pid, + nlh->nlmsg_seq, 0, nlh); + if (err < 0) { + WARN_ON(err == -EMSGSIZE); + kfree_skb(rep); + goto out; + } err = netlink_unicast(idiagnl, rep, NETLINK_CB(in_skb).pid, MSG_DONTWAIT); if (err > 0) @@ -294,7 +299,7 @@ out: return err; } -static int bitstring_match(const u32 *a1, const u32 *a2, int bits) +static int bitstring_match(const __be32 *a1, const __be32 *a2, int bits) { int words = bits >> 5; @@ -305,8 +310,8 @@ static int bitstring_match(const u32 *a1, const u32 *a2, int bits) return 0; } if (bits) { - __u32 w1, w2; - __u32 mask; + __be32 w1, w2; + __be32 mask; w1 = a1[words]; w2 = a2[words]; @@ -352,7 +357,7 @@ static int inet_diag_bc_run(const void *bc, int len, case INET_DIAG_BC_S_COND: case INET_DIAG_BC_D_COND: { struct inet_diag_hostcond *cond; - u32 *addr; + __be32 *addr; cond = (struct inet_diag_hostcond *)(op + 1); if (cond->port != -1 && @@ -378,7 +383,7 @@ static int inet_diag_bc_run(const void *bc, int len, if (addr[0] == 0 && addr[1] == 0 && addr[2] == htonl(0xffff) && bitstring_match(addr + 3, cond->addr, - cond->prefix_len)) + cond->prefix_len)) break; } yes = 0; @@ -515,7 +520,7 @@ static int inet_twsk_diag_dump(struct inet_timewait_sock *tw, } entry.sport = tw->tw_num; entry.dport = ntohs(tw->tw_dport); - entry.userlocks = 0; + entry.userlocks = 0; if (!inet_diag_bc_run(RTA_DATA(bc), RTA_PAYLOAD(bc), &entry)) return 0; @@ -532,7 +537,7 @@ static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, { const struct inet_request_sock *ireq = inet_rsk(req); struct inet_sock *inet = inet_sk(sk); - unsigned char *b = skb->tail; + unsigned char *b = skb_tail_pointer(skb); struct inet_diag_msg *r; struct nlmsghdr *nlh; long tmo; @@ -571,12 +576,12 @@ static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, &inet6_rsk(req)->rmt_addr); } #endif - nlh->nlmsg_len = skb->tail - b; + nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len; nlmsg_failure: - skb_trim(skb, b - skb->data); + nlmsg_trim(skb, b); return -1; } @@ -742,13 +747,14 @@ skip_listen_ht: for (i = s_i; i < hashinfo->ehash_size; i++) { struct inet_ehash_bucket *head = &hashinfo->ehash[i]; + rwlock_t *lock = inet_ehash_lockp(hashinfo, i); struct sock *sk; struct hlist_node *node; if (i > s_i) s_num = 0; - read_lock_bh(&head->lock); + read_lock_bh(lock); num = 0; sk_for_each(sk, node, &head->chain) { struct inet_sock *inet = inet_sk(sk); @@ -764,7 +770,7 @@ skip_listen_ht: r->id.idiag_dport) goto next_normal; if (inet_csk_diag_dump(sk, skb, cb) < 0) { - read_unlock_bh(&head->lock); + read_unlock_bh(lock); goto done; } next_normal: @@ -775,7 +781,7 @@ next_normal: struct inet_timewait_sock *tw; inet_twsk_for_each(tw, node, - &hashinfo->ehash[i + hashinfo->ehash_size].chain) { + &head->twchain) { if (num < s_num) goto next_dying; @@ -786,14 +792,14 @@ next_normal: r->id.idiag_dport) goto next_dying; if (inet_twsk_diag_dump(tw, skb, cb) < 0) { - read_unlock_bh(&head->lock); + read_unlock_bh(lock); goto done; } next_dying: ++num; } } - read_unlock_bh(&head->lock); + read_unlock_bh(lock); } done: @@ -802,68 +808,49 @@ done: return skb->len; } -static inline int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) +static int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { - if (!(nlh->nlmsg_flags&NLM_F_REQUEST)) - return 0; + int hdrlen = sizeof(struct inet_diag_req); - if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX) - goto err_inval; + if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX || + nlmsg_len(nlh) < hdrlen) + return -EINVAL; + +#ifdef CONFIG_KMOD + if (inet_diag_table[nlh->nlmsg_type] == NULL) + request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK, + NETLINK_INET_DIAG, nlh->nlmsg_type); +#endif if (inet_diag_table[nlh->nlmsg_type] == NULL) return -ENOENT; - if (NLMSG_LENGTH(sizeof(struct inet_diag_req)) > skb->len) - goto err_inval; - - if (nlh->nlmsg_flags&NLM_F_DUMP) { - if (nlh->nlmsg_len > - (4 + NLMSG_SPACE(sizeof(struct inet_diag_req)))) { - struct rtattr *rta = (void *)(NLMSG_DATA(nlh) + - sizeof(struct inet_diag_req)); - if (rta->rta_type != INET_DIAG_REQ_BYTECODE || - rta->rta_len < 8 || - rta->rta_len > - (nlh->nlmsg_len - - NLMSG_SPACE(sizeof(struct inet_diag_req)))) - goto err_inval; - if (inet_diag_bc_audit(RTA_DATA(rta), RTA_PAYLOAD(rta))) - goto err_inval; + if (nlh->nlmsg_flags & NLM_F_DUMP) { + if (nlmsg_attrlen(nlh, hdrlen)) { + struct nlattr *attr; + + attr = nlmsg_find_attr(nlh, hdrlen, + INET_DIAG_REQ_BYTECODE); + if (attr == NULL || + nla_len(attr) < sizeof(struct inet_diag_bc_op) || + inet_diag_bc_audit(nla_data(attr), nla_len(attr))) + return -EINVAL; } + return netlink_dump_start(idiagnl, skb, nlh, inet_diag_dump, NULL); - } else - return inet_diag_get_exact(skb, nlh); + } -err_inval: - return -EINVAL; + return inet_diag_get_exact(skb, nlh); } +static DEFINE_MUTEX(inet_diag_mutex); -static inline void inet_diag_rcv_skb(struct sk_buff *skb) +static void inet_diag_rcv(struct sk_buff *skb) { - if (skb->len >= NLMSG_SPACE(0)) { - int err; - struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data; - - if (nlh->nlmsg_len < sizeof(*nlh) || - skb->len < nlh->nlmsg_len) - return; - err = inet_diag_rcv_msg(skb, nlh); - if (err || nlh->nlmsg_flags & NLM_F_ACK) - netlink_ack(skb, nlh, err); - } -} - -static void inet_diag_rcv(struct sock *sk, int len) -{ - struct sk_buff *skb; - unsigned int qlen = skb_queue_len(&sk->sk_receive_queue); - - while (qlen-- && (skb = skb_dequeue(&sk->sk_receive_queue))) { - inet_diag_rcv_skb(skb); - kfree_skb(skb); - } + mutex_lock(&inet_diag_mutex); + netlink_rcv_skb(skb, &inet_diag_rcv_msg); + mutex_unlock(&inet_diag_mutex); } static DEFINE_SPINLOCK(inet_diag_register_lock); @@ -913,8 +900,8 @@ static int __init inet_diag_init(void) if (!inet_diag_table) goto out; - idiagnl = netlink_kernel_create(NETLINK_INET_DIAG, 0, inet_diag_rcv, - THIS_MODULE); + idiagnl = netlink_kernel_create(&init_net, NETLINK_INET_DIAG, 0, + inet_diag_rcv, NULL, THIS_MODULE); if (idiagnl == NULL) goto out_free_table; err = 0; @@ -934,3 +921,4 @@ static void __exit inet_diag_exit(void) module_init(inet_diag_init); module_exit(inet_diag_exit); MODULE_LICENSE("GPL"); +MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_INET_DIAG);