diff options
Diffstat (limited to 'net/packet/diag.c')
-rw-r--r-- | net/packet/diag.c | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/net/packet/diag.c b/net/packet/diag.c new file mode 100644 index 000000000000..8db6e21c46bd --- /dev/null +++ b/net/packet/diag.c | |||
@@ -0,0 +1,242 @@ | |||
1 | #include <linux/module.h> | ||
2 | #include <linux/sock_diag.h> | ||
3 | #include <linux/net.h> | ||
4 | #include <linux/netdevice.h> | ||
5 | #include <linux/packet_diag.h> | ||
6 | #include <net/net_namespace.h> | ||
7 | #include <net/sock.h> | ||
8 | |||
9 | #include "internal.h" | ||
10 | |||
11 | static int pdiag_put_info(const struct packet_sock *po, struct sk_buff *nlskb) | ||
12 | { | ||
13 | struct packet_diag_info pinfo; | ||
14 | |||
15 | pinfo.pdi_index = po->ifindex; | ||
16 | pinfo.pdi_version = po->tp_version; | ||
17 | pinfo.pdi_reserve = po->tp_reserve; | ||
18 | pinfo.pdi_copy_thresh = po->copy_thresh; | ||
19 | pinfo.pdi_tstamp = po->tp_tstamp; | ||
20 | |||
21 | pinfo.pdi_flags = 0; | ||
22 | if (po->running) | ||
23 | pinfo.pdi_flags |= PDI_RUNNING; | ||
24 | if (po->auxdata) | ||
25 | pinfo.pdi_flags |= PDI_AUXDATA; | ||
26 | if (po->origdev) | ||
27 | pinfo.pdi_flags |= PDI_ORIGDEV; | ||
28 | if (po->has_vnet_hdr) | ||
29 | pinfo.pdi_flags |= PDI_VNETHDR; | ||
30 | if (po->tp_loss) | ||
31 | pinfo.pdi_flags |= PDI_LOSS; | ||
32 | |||
33 | return nla_put(nlskb, PACKET_DIAG_INFO, sizeof(pinfo), &pinfo); | ||
34 | } | ||
35 | |||
36 | static int pdiag_put_mclist(const struct packet_sock *po, struct sk_buff *nlskb) | ||
37 | { | ||
38 | struct nlattr *mca; | ||
39 | struct packet_mclist *ml; | ||
40 | |||
41 | mca = nla_nest_start(nlskb, PACKET_DIAG_MCLIST); | ||
42 | if (!mca) | ||
43 | return -EMSGSIZE; | ||
44 | |||
45 | rtnl_lock(); | ||
46 | for (ml = po->mclist; ml; ml = ml->next) { | ||
47 | struct packet_diag_mclist *dml; | ||
48 | |||
49 | dml = nla_reserve_nohdr(nlskb, sizeof(*dml)); | ||
50 | if (!dml) { | ||
51 | rtnl_unlock(); | ||
52 | nla_nest_cancel(nlskb, mca); | ||
53 | return -EMSGSIZE; | ||
54 | } | ||
55 | |||
56 | dml->pdmc_index = ml->ifindex; | ||
57 | dml->pdmc_type = ml->type; | ||
58 | dml->pdmc_alen = ml->alen; | ||
59 | dml->pdmc_count = ml->count; | ||
60 | BUILD_BUG_ON(sizeof(dml->pdmc_addr) != sizeof(ml->addr)); | ||
61 | memcpy(dml->pdmc_addr, ml->addr, sizeof(ml->addr)); | ||
62 | } | ||
63 | |||
64 | rtnl_unlock(); | ||
65 | nla_nest_end(nlskb, mca); | ||
66 | |||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | static int pdiag_put_ring(struct packet_ring_buffer *ring, int ver, int nl_type, | ||
71 | struct sk_buff *nlskb) | ||
72 | { | ||
73 | struct packet_diag_ring pdr; | ||
74 | |||
75 | if (!ring->pg_vec || ((ver > TPACKET_V2) && | ||
76 | (nl_type == PACKET_DIAG_TX_RING))) | ||
77 | return 0; | ||
78 | |||
79 | pdr.pdr_block_size = ring->pg_vec_pages << PAGE_SHIFT; | ||
80 | pdr.pdr_block_nr = ring->pg_vec_len; | ||
81 | pdr.pdr_frame_size = ring->frame_size; | ||
82 | pdr.pdr_frame_nr = ring->frame_max + 1; | ||
83 | |||
84 | if (ver > TPACKET_V2) { | ||
85 | pdr.pdr_retire_tmo = ring->prb_bdqc.retire_blk_tov; | ||
86 | pdr.pdr_sizeof_priv = ring->prb_bdqc.blk_sizeof_priv; | ||
87 | pdr.pdr_features = ring->prb_bdqc.feature_req_word; | ||
88 | } else { | ||
89 | pdr.pdr_retire_tmo = 0; | ||
90 | pdr.pdr_sizeof_priv = 0; | ||
91 | pdr.pdr_features = 0; | ||
92 | } | ||
93 | |||
94 | return nla_put(nlskb, nl_type, sizeof(pdr), &pdr); | ||
95 | } | ||
96 | |||
97 | static int pdiag_put_rings_cfg(struct packet_sock *po, struct sk_buff *skb) | ||
98 | { | ||
99 | int ret; | ||
100 | |||
101 | mutex_lock(&po->pg_vec_lock); | ||
102 | ret = pdiag_put_ring(&po->rx_ring, po->tp_version, | ||
103 | PACKET_DIAG_RX_RING, skb); | ||
104 | if (!ret) | ||
105 | ret = pdiag_put_ring(&po->tx_ring, po->tp_version, | ||
106 | PACKET_DIAG_TX_RING, skb); | ||
107 | mutex_unlock(&po->pg_vec_lock); | ||
108 | |||
109 | return ret; | ||
110 | } | ||
111 | |||
112 | static int pdiag_put_fanout(struct packet_sock *po, struct sk_buff *nlskb) | ||
113 | { | ||
114 | int ret = 0; | ||
115 | |||
116 | mutex_lock(&fanout_mutex); | ||
117 | if (po->fanout) { | ||
118 | u32 val; | ||
119 | |||
120 | val = (u32)po->fanout->id | ((u32)po->fanout->type << 16); | ||
121 | ret = nla_put_u32(nlskb, PACKET_DIAG_FANOUT, val); | ||
122 | } | ||
123 | mutex_unlock(&fanout_mutex); | ||
124 | |||
125 | return ret; | ||
126 | } | ||
127 | |||
128 | static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct packet_diag_req *req, | ||
129 | u32 portid, u32 seq, u32 flags, int sk_ino) | ||
130 | { | ||
131 | struct nlmsghdr *nlh; | ||
132 | struct packet_diag_msg *rp; | ||
133 | struct packet_sock *po = pkt_sk(sk); | ||
134 | |||
135 | nlh = nlmsg_put(skb, portid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rp), flags); | ||
136 | if (!nlh) | ||
137 | return -EMSGSIZE; | ||
138 | |||
139 | rp = nlmsg_data(nlh); | ||
140 | rp->pdiag_family = AF_PACKET; | ||
141 | rp->pdiag_type = sk->sk_type; | ||
142 | rp->pdiag_num = ntohs(po->num); | ||
143 | rp->pdiag_ino = sk_ino; | ||
144 | sock_diag_save_cookie(sk, rp->pdiag_cookie); | ||
145 | |||
146 | if ((req->pdiag_show & PACKET_SHOW_INFO) && | ||
147 | pdiag_put_info(po, skb)) | ||
148 | goto out_nlmsg_trim; | ||
149 | |||
150 | if ((req->pdiag_show & PACKET_SHOW_MCLIST) && | ||
151 | pdiag_put_mclist(po, skb)) | ||
152 | goto out_nlmsg_trim; | ||
153 | |||
154 | if ((req->pdiag_show & PACKET_SHOW_RING_CFG) && | ||
155 | pdiag_put_rings_cfg(po, skb)) | ||
156 | goto out_nlmsg_trim; | ||
157 | |||
158 | if ((req->pdiag_show & PACKET_SHOW_FANOUT) && | ||
159 | pdiag_put_fanout(po, skb)) | ||
160 | goto out_nlmsg_trim; | ||
161 | |||
162 | return nlmsg_end(skb, nlh); | ||
163 | |||
164 | out_nlmsg_trim: | ||
165 | nlmsg_cancel(skb, nlh); | ||
166 | return -EMSGSIZE; | ||
167 | } | ||
168 | |||
169 | static int packet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) | ||
170 | { | ||
171 | int num = 0, s_num = cb->args[0]; | ||
172 | struct packet_diag_req *req; | ||
173 | struct net *net; | ||
174 | struct sock *sk; | ||
175 | struct hlist_node *node; | ||
176 | |||
177 | net = sock_net(skb->sk); | ||
178 | req = nlmsg_data(cb->nlh); | ||
179 | |||
180 | mutex_lock(&net->packet.sklist_lock); | ||
181 | sk_for_each(sk, node, &net->packet.sklist) { | ||
182 | if (!net_eq(sock_net(sk), net)) | ||
183 | continue; | ||
184 | if (num < s_num) | ||
185 | goto next; | ||
186 | |||
187 | if (sk_diag_fill(sk, skb, req, NETLINK_CB(cb->skb).portid, | ||
188 | cb->nlh->nlmsg_seq, NLM_F_MULTI, | ||
189 | sock_i_ino(sk)) < 0) | ||
190 | goto done; | ||
191 | next: | ||
192 | num++; | ||
193 | } | ||
194 | done: | ||
195 | mutex_unlock(&net->packet.sklist_lock); | ||
196 | cb->args[0] = num; | ||
197 | |||
198 | return skb->len; | ||
199 | } | ||
200 | |||
201 | static int packet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h) | ||
202 | { | ||
203 | int hdrlen = sizeof(struct packet_diag_req); | ||
204 | struct net *net = sock_net(skb->sk); | ||
205 | struct packet_diag_req *req; | ||
206 | |||
207 | if (nlmsg_len(h) < hdrlen) | ||
208 | return -EINVAL; | ||
209 | |||
210 | req = nlmsg_data(h); | ||
211 | /* Make it possible to support protocol filtering later */ | ||
212 | if (req->sdiag_protocol) | ||
213 | return -EINVAL; | ||
214 | |||
215 | if (h->nlmsg_flags & NLM_F_DUMP) { | ||
216 | struct netlink_dump_control c = { | ||
217 | .dump = packet_diag_dump, | ||
218 | }; | ||
219 | return netlink_dump_start(net->diag_nlsk, skb, h, &c); | ||
220 | } else | ||
221 | return -EOPNOTSUPP; | ||
222 | } | ||
223 | |||
224 | static const struct sock_diag_handler packet_diag_handler = { | ||
225 | .family = AF_PACKET, | ||
226 | .dump = packet_diag_handler_dump, | ||
227 | }; | ||
228 | |||
229 | static int __init packet_diag_init(void) | ||
230 | { | ||
231 | return sock_diag_register(&packet_diag_handler); | ||
232 | } | ||
233 | |||
234 | static void __exit packet_diag_exit(void) | ||
235 | { | ||
236 | sock_diag_unregister(&packet_diag_handler); | ||
237 | } | ||
238 | |||
239 | module_init(packet_diag_init); | ||
240 | module_exit(packet_diag_exit); | ||
241 | MODULE_LICENSE("GPL"); | ||
242 | MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 17 /* AF_PACKET */); | ||