linux/net/sctp/sctp_diag.c
<<
>>
Prefs
   1#include <linux/module.h>
   2#include <linux/inet_diag.h>
   3#include <linux/sock_diag.h>
   4#include <net/sctp/sctp.h>
   5
   6static void sctp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
   7                               void *info);
   8
   9/* define some functions to make asoc/ep fill look clean */
  10static void inet_diag_msg_sctpasoc_fill(struct inet_diag_msg *r,
  11                                        struct sock *sk,
  12                                        struct sctp_association *asoc)
  13{
  14        union sctp_addr laddr, paddr;
  15        struct dst_entry *dst;
  16
  17        laddr = list_entry(asoc->base.bind_addr.address_list.next,
  18                           struct sctp_sockaddr_entry, list)->a;
  19        paddr = asoc->peer.primary_path->ipaddr;
  20        dst = asoc->peer.primary_path->dst;
  21
  22        r->idiag_family = sk->sk_family;
  23        r->id.idiag_sport = htons(asoc->base.bind_addr.port);
  24        r->id.idiag_dport = htons(asoc->peer.port);
  25        r->id.idiag_if = dst ? dst->dev->ifindex : 0;
  26        sock_diag_save_cookie(sk, r->id.idiag_cookie);
  27
  28#if IS_ENABLED(CONFIG_IPV6)
  29        if (sk->sk_family == AF_INET6) {
  30                *(struct in6_addr *)r->id.idiag_src = laddr.v6.sin6_addr;
  31                *(struct in6_addr *)r->id.idiag_dst = paddr.v6.sin6_addr;
  32        } else
  33#endif
  34        {
  35                memset(&r->id.idiag_src, 0, sizeof(r->id.idiag_src));
  36                memset(&r->id.idiag_dst, 0, sizeof(r->id.idiag_dst));
  37
  38                r->id.idiag_src[0] = laddr.v4.sin_addr.s_addr;
  39                r->id.idiag_dst[0] = paddr.v4.sin_addr.s_addr;
  40        }
  41
  42        r->idiag_state = asoc->state;
  43        r->idiag_timer = SCTP_EVENT_TIMEOUT_T3_RTX;
  44        r->idiag_retrans = asoc->rtx_data_chunks;
  45        r->idiag_expires = jiffies_to_msecs(
  46                asoc->timeouts[SCTP_EVENT_TIMEOUT_T3_RTX] - jiffies);
  47}
  48
  49static int inet_diag_msg_sctpladdrs_fill(struct sk_buff *skb,
  50                                         struct list_head *address_list)
  51{
  52        struct sctp_sockaddr_entry *laddr;
  53        int addrlen = sizeof(struct sockaddr_storage);
  54        int addrcnt = 0;
  55        struct nlattr *attr;
  56        void *info = NULL;
  57
  58        list_for_each_entry_rcu(laddr, address_list, list)
  59                addrcnt++;
  60
  61        attr = nla_reserve(skb, INET_DIAG_LOCALS, addrlen * addrcnt);
  62        if (!attr)
  63                return -EMSGSIZE;
  64
  65        info = nla_data(attr);
  66        list_for_each_entry_rcu(laddr, address_list, list) {
  67                memcpy(info, &laddr->a, addrlen);
  68                info += addrlen;
  69        }
  70
  71        return 0;
  72}
  73
  74static int inet_diag_msg_sctpaddrs_fill(struct sk_buff *skb,
  75                                        struct sctp_association *asoc)
  76{
  77        int addrlen = sizeof(struct sockaddr_storage);
  78        struct sctp_transport *from;
  79        struct nlattr *attr;
  80        void *info = NULL;
  81
  82        attr = nla_reserve(skb, INET_DIAG_PEERS,
  83                           addrlen * asoc->peer.transport_count);
  84        if (!attr)
  85                return -EMSGSIZE;
  86
  87        info = nla_data(attr);
  88        list_for_each_entry(from, &asoc->peer.transport_addr_list,
  89                            transports) {
  90                memcpy(info, &from->ipaddr, addrlen);
  91                info += addrlen;
  92        }
  93
  94        return 0;
  95}
  96
  97/* sctp asoc/ep fill*/
  98static int inet_sctp_diag_fill(struct sock *sk, struct sctp_association *asoc,
  99                               struct sk_buff *skb,
 100                               const struct inet_diag_req_v2 *req,
 101                               struct user_namespace *user_ns,
 102                               int portid, u32 seq, u16 nlmsg_flags,
 103                               const struct nlmsghdr *unlh)
 104{
 105        struct sctp_endpoint *ep = sctp_sk(sk)->ep;
 106        struct list_head *addr_list;
 107        struct inet_diag_msg *r;
 108        struct nlmsghdr  *nlh;
 109        int ext = req->idiag_ext;
 110        struct sctp_infox infox;
 111        void *info = NULL;
 112
 113        nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r),
 114                        nlmsg_flags);
 115        if (!nlh)
 116                return -EMSGSIZE;
 117
 118        r = nlmsg_data(nlh);
 119        BUG_ON(!sk_fullsock(sk));
 120
 121        if (asoc) {
 122                inet_diag_msg_sctpasoc_fill(r, sk, asoc);
 123        } else {
 124                inet_diag_msg_common_fill(r, sk);
 125                r->idiag_state = sk->sk_state;
 126                r->idiag_timer = 0;
 127                r->idiag_retrans = 0;
 128        }
 129
 130        if (inet_diag_msg_attrs_fill(sk, skb, r, ext, user_ns))
 131                goto errout;
 132
 133        if (ext & (1 << (INET_DIAG_SKMEMINFO - 1))) {
 134                u32 mem[SK_MEMINFO_VARS];
 135                int amt;
 136
 137                if (asoc && asoc->ep->sndbuf_policy)
 138                        amt = asoc->sndbuf_used;
 139                else
 140                        amt = sk_wmem_alloc_get(sk);
 141                mem[SK_MEMINFO_WMEM_ALLOC] = amt;
 142                if (asoc && asoc->ep->rcvbuf_policy)
 143                        amt = atomic_read(&asoc->rmem_alloc);
 144                else
 145                        amt = sk_rmem_alloc_get(sk);
 146                mem[SK_MEMINFO_RMEM_ALLOC] = amt;
 147                mem[SK_MEMINFO_RCVBUF] = sk->sk_rcvbuf;
 148                mem[SK_MEMINFO_SNDBUF] = sk->sk_sndbuf;
 149                mem[SK_MEMINFO_FWD_ALLOC] = sk->sk_forward_alloc;
 150                mem[SK_MEMINFO_WMEM_QUEUED] = sk->sk_wmem_queued;
 151                mem[SK_MEMINFO_OPTMEM] = atomic_read(&sk->sk_omem_alloc);
 152                mem[SK_MEMINFO_BACKLOG] = sk->sk_backlog.len;
 153                mem[SK_MEMINFO_DROPS] = atomic_read(&sk->sk_drops);
 154
 155                if (nla_put(skb, INET_DIAG_SKMEMINFO, sizeof(mem), &mem) < 0)
 156                        goto errout;
 157        }
 158
 159        if (ext & (1 << (INET_DIAG_INFO - 1))) {
 160                struct nlattr *attr;
 161
 162                attr = nla_reserve_64bit(skb, INET_DIAG_INFO,
 163                                         sizeof(struct sctp_info),
 164                                         INET_DIAG_PAD);
 165                if (!attr)
 166                        goto errout;
 167
 168                info = nla_data(attr);
 169        }
 170        infox.sctpinfo = (struct sctp_info *)info;
 171        infox.asoc = asoc;
 172        sctp_diag_get_info(sk, r, &infox);
 173
 174        addr_list = asoc ? &asoc->base.bind_addr.address_list
 175                         : &ep->base.bind_addr.address_list;
 176        if (inet_diag_msg_sctpladdrs_fill(skb, addr_list))
 177                goto errout;
 178
 179        if (asoc && (ext & (1 << (INET_DIAG_CONG - 1))))
 180                if (nla_put_string(skb, INET_DIAG_CONG, "reno") < 0)
 181                        goto errout;
 182
 183        if (asoc && inet_diag_msg_sctpaddrs_fill(skb, asoc))
 184                goto errout;
 185
 186        nlmsg_end(skb, nlh);
 187        return 0;
 188
 189errout:
 190        nlmsg_cancel(skb, nlh);
 191        return -EMSGSIZE;
 192}
 193
 194/* callback and param */
 195struct sctp_comm_param {
 196        struct sk_buff *skb;
 197        struct netlink_callback *cb;
 198        const struct inet_diag_req_v2 *r;
 199        const struct nlmsghdr *nlh;
 200};
 201
 202static size_t inet_assoc_attr_size(struct sctp_association *asoc)
 203{
 204        int addrlen = sizeof(struct sockaddr_storage);
 205        int addrcnt = 0;
 206        struct sctp_sockaddr_entry *laddr;
 207
 208        list_for_each_entry_rcu(laddr, &asoc->base.bind_addr.address_list,
 209                                list)
 210                addrcnt++;
 211
 212        return    nla_total_size(sizeof(struct sctp_info))
 213                + nla_total_size(1) /* INET_DIAG_SHUTDOWN */
 214                + nla_total_size(1) /* INET_DIAG_TOS */
 215                + nla_total_size(1) /* INET_DIAG_TCLASS */
 216                + nla_total_size(addrlen * asoc->peer.transport_count)
 217                + nla_total_size(addrlen * addrcnt)
 218                + nla_total_size(sizeof(struct inet_diag_meminfo))
 219                + nla_total_size(sizeof(struct inet_diag_msg))
 220                + 64;
 221}
 222
 223static int sctp_tsp_dump_one(struct sctp_transport *tsp, void *p)
 224{
 225        struct sctp_association *assoc = tsp->asoc;
 226        struct sock *sk = tsp->asoc->base.sk;
 227        struct sctp_comm_param *commp = p;
 228        struct sk_buff *in_skb = commp->skb;
 229        const struct inet_diag_req_v2 *req = commp->r;
 230        const struct nlmsghdr *nlh = commp->nlh;
 231        struct net *net = sock_net(in_skb->sk);
 232        struct sk_buff *rep;
 233        int err;
 234
 235        err = sock_diag_check_cookie(sk, req->id.idiag_cookie);
 236        if (err)
 237                goto out;
 238
 239        err = -ENOMEM;
 240        rep = nlmsg_new(inet_assoc_attr_size(assoc), GFP_KERNEL);
 241        if (!rep)
 242                goto out;
 243
 244        lock_sock(sk);
 245        if (sk != assoc->base.sk) {
 246                release_sock(sk);
 247                sk = assoc->base.sk;
 248                lock_sock(sk);
 249        }
 250        err = inet_sctp_diag_fill(sk, assoc, rep, req,
 251                                  sk_user_ns(NETLINK_CB(in_skb).sk),
 252                                  NETLINK_CB(in_skb).portid,
 253                                  nlh->nlmsg_seq, 0, nlh);
 254        release_sock(sk);
 255        if (err < 0) {
 256                WARN_ON(err == -EMSGSIZE);
 257                kfree_skb(rep);
 258                goto out;
 259        }
 260
 261        err = netlink_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).portid,
 262                              MSG_DONTWAIT);
 263        if (err > 0)
 264                err = 0;
 265out:
 266        return err;
 267}
 268
 269static int sctp_tsp_dump(struct sctp_transport *tsp, void *p)
 270{
 271        struct sctp_endpoint *ep = tsp->asoc->ep;
 272        struct sctp_comm_param *commp = p;
 273        struct sock *sk = ep->base.sk;
 274        struct sk_buff *skb = commp->skb;
 275        struct netlink_callback *cb = commp->cb;
 276        const struct inet_diag_req_v2 *r = commp->r;
 277        struct sctp_association *assoc =
 278                list_entry(ep->asocs.next, struct sctp_association, asocs);
 279        int err = 0;
 280
 281        /* find the ep only once through the transports by this condition */
 282        if (tsp->asoc != assoc)
 283                goto out;
 284
 285        if (r->sdiag_family != AF_UNSPEC && sk->sk_family != r->sdiag_family)
 286                goto out;
 287
 288        lock_sock(sk);
 289        if (sk != assoc->base.sk)
 290                goto release;
 291        list_for_each_entry(assoc, &ep->asocs, asocs) {
 292                if (cb->args[4] < cb->args[1])
 293                        goto next;
 294
 295                if (r->id.idiag_sport != htons(assoc->base.bind_addr.port) &&
 296                    r->id.idiag_sport)
 297                        goto next;
 298                if (r->id.idiag_dport != htons(assoc->peer.port) &&
 299                    r->id.idiag_dport)
 300                        goto next;
 301
 302                if (!cb->args[3] &&
 303                    inet_sctp_diag_fill(sk, NULL, skb, r,
 304                                        sk_user_ns(NETLINK_CB(cb->skb).sk),
 305                                        NETLINK_CB(cb->skb).portid,
 306                                        cb->nlh->nlmsg_seq,
 307                                        NLM_F_MULTI, cb->nlh) < 0) {
 308                        cb->args[3] = 1;
 309                        err = 2;
 310                        goto release;
 311                }
 312                cb->args[3] = 1;
 313
 314                if (inet_sctp_diag_fill(sk, assoc, skb, r,
 315                                        sk_user_ns(NETLINK_CB(cb->skb).sk),
 316                                        NETLINK_CB(cb->skb).portid,
 317                                        cb->nlh->nlmsg_seq, 0, cb->nlh) < 0) {
 318                        err = 2;
 319                        goto release;
 320                }
 321next:
 322                cb->args[4]++;
 323        }
 324        cb->args[1] = 0;
 325        cb->args[2]++;
 326        cb->args[3] = 0;
 327        cb->args[4] = 0;
 328release:
 329        release_sock(sk);
 330        return err;
 331out:
 332        cb->args[2]++;
 333        return err;
 334}
 335
 336static int sctp_ep_dump(struct sctp_endpoint *ep, void *p)
 337{
 338        struct sctp_comm_param *commp = p;
 339        struct sock *sk = ep->base.sk;
 340        struct sk_buff *skb = commp->skb;
 341        struct netlink_callback *cb = commp->cb;
 342        const struct inet_diag_req_v2 *r = commp->r;
 343        struct net *net = sock_net(skb->sk);
 344        struct inet_sock *inet = inet_sk(sk);
 345        int err = 0;
 346
 347        if (!net_eq(sock_net(sk), net))
 348                goto out;
 349
 350        if (cb->args[4] < cb->args[1])
 351                goto next;
 352
 353        if ((r->idiag_states & ~TCPF_LISTEN) && !list_empty(&ep->asocs))
 354                goto next;
 355
 356        if (r->sdiag_family != AF_UNSPEC &&
 357            sk->sk_family != r->sdiag_family)
 358                goto next;
 359
 360        if (r->id.idiag_sport != inet->inet_sport &&
 361            r->id.idiag_sport)
 362                goto next;
 363
 364        if (r->id.idiag_dport != inet->inet_dport &&
 365            r->id.idiag_dport)
 366                goto next;
 367
 368        if (inet_sctp_diag_fill(sk, NULL, skb, r,
 369                                sk_user_ns(NETLINK_CB(cb->skb).sk),
 370                                NETLINK_CB(cb->skb).portid,
 371                                cb->nlh->nlmsg_seq, NLM_F_MULTI,
 372                                cb->nlh) < 0) {
 373                err = 2;
 374                goto out;
 375        }
 376next:
 377        cb->args[4]++;
 378out:
 379        return err;
 380}
 381
 382/* define the functions for sctp_diag_handler*/
 383static void sctp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
 384                               void *info)
 385{
 386        struct sctp_infox *infox = (struct sctp_infox *)info;
 387
 388        if (infox->asoc) {
 389                r->idiag_rqueue = atomic_read(&infox->asoc->rmem_alloc);
 390                r->idiag_wqueue = infox->asoc->sndbuf_used;
 391        } else {
 392                r->idiag_rqueue = sk->sk_ack_backlog;
 393                r->idiag_wqueue = sk->sk_max_ack_backlog;
 394        }
 395        if (infox->sctpinfo)
 396                sctp_get_sctp_info(sk, infox->asoc, infox->sctpinfo);
 397}
 398
 399static int sctp_diag_dump_one(struct sk_buff *in_skb,
 400                              const struct nlmsghdr *nlh,
 401                              const struct inet_diag_req_v2 *req)
 402{
 403        struct net *net = sock_net(in_skb->sk);
 404        union sctp_addr laddr, paddr;
 405        struct sctp_comm_param commp = {
 406                .skb = in_skb,
 407                .r = req,
 408                .nlh = nlh,
 409        };
 410
 411        if (req->sdiag_family == AF_INET) {
 412                laddr.v4.sin_port = req->id.idiag_sport;
 413                laddr.v4.sin_addr.s_addr = req->id.idiag_src[0];
 414                laddr.v4.sin_family = AF_INET;
 415
 416                paddr.v4.sin_port = req->id.idiag_dport;
 417                paddr.v4.sin_addr.s_addr = req->id.idiag_dst[0];
 418                paddr.v4.sin_family = AF_INET;
 419        } else {
 420                laddr.v6.sin6_port = req->id.idiag_sport;
 421                memcpy(&laddr.v6.sin6_addr, req->id.idiag_src, 64);
 422                laddr.v6.sin6_family = AF_INET6;
 423
 424                paddr.v6.sin6_port = req->id.idiag_dport;
 425                memcpy(&paddr.v6.sin6_addr, req->id.idiag_dst, 64);
 426                paddr.v6.sin6_family = AF_INET6;
 427        }
 428
 429        return sctp_transport_lookup_process(sctp_tsp_dump_one,
 430                                             net, &laddr, &paddr, &commp);
 431}
 432
 433static void sctp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
 434                           const struct inet_diag_req_v2 *r, struct nlattr *bc)
 435{
 436        u32 idiag_states = r->idiag_states;
 437        struct net *net = sock_net(skb->sk);
 438        struct sctp_comm_param commp = {
 439                .skb = skb,
 440                .cb = cb,
 441                .r = r,
 442        };
 443
 444        /* eps hashtable dumps
 445         * args:
 446         * 0 : if it will traversal listen sock
 447         * 1 : to record the sock pos of this time's traversal
 448         * 4 : to work as a temporary variable to traversal list
 449         */
 450        if (cb->args[0] == 0) {
 451                if (!(idiag_states & TCPF_LISTEN))
 452                        goto skip;
 453                if (sctp_for_each_endpoint(sctp_ep_dump, &commp))
 454                        goto done;
 455skip:
 456                cb->args[0] = 1;
 457                cb->args[1] = 0;
 458                cb->args[4] = 0;
 459        }
 460
 461        /* asocs by transport hashtable dump
 462         * args:
 463         * 1 : to record the assoc pos of this time's traversal
 464         * 2 : to record the transport pos of this time's traversal
 465         * 3 : to mark if we have dumped the ep info of the current asoc
 466         * 4 : to work as a temporary variable to traversal list
 467         */
 468        if (!(idiag_states & ~TCPF_LISTEN))
 469                goto done;
 470        sctp_for_each_transport(sctp_tsp_dump, net, cb->args[2], &commp);
 471done:
 472        cb->args[1] = cb->args[4];
 473        cb->args[4] = 0;
 474}
 475
 476static const struct inet_diag_handler sctp_diag_handler = {
 477        .dump            = sctp_diag_dump,
 478        .dump_one        = sctp_diag_dump_one,
 479        .idiag_get_info  = sctp_diag_get_info,
 480        .idiag_type      = IPPROTO_SCTP,
 481        .idiag_info_size = sizeof(struct sctp_info),
 482};
 483
 484static int __init sctp_diag_init(void)
 485{
 486        return inet_diag_register(&sctp_diag_handler);
 487}
 488
 489static void __exit sctp_diag_exit(void)
 490{
 491        inet_diag_unregister(&sctp_diag_handler);
 492}
 493
 494module_init(sctp_diag_init);
 495module_exit(sctp_diag_exit);
 496MODULE_LICENSE("GPL");
 497MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-132);
 498