diff options
Diffstat (limited to 'net/tipc')
| -rw-r--r-- | net/tipc/Makefile | 2 | ||||
| -rw-r--r-- | net/tipc/group.c | 404 | ||||
| -rw-r--r-- | net/tipc/group.h | 64 | ||||
| -rw-r--r-- | net/tipc/link.c | 3 | ||||
| -rw-r--r-- | net/tipc/msg.h | 50 | ||||
| -rw-r--r-- | net/tipc/name_table.c | 44 | ||||
| -rw-r--r-- | net/tipc/name_table.h | 3 | ||||
| -rw-r--r-- | net/tipc/node.h | 3 | ||||
| -rw-r--r-- | net/tipc/socket.c | 209 |
9 files changed, 734 insertions, 48 deletions
diff --git a/net/tipc/Makefile b/net/tipc/Makefile index 31b9f9c52974..a3af73ec0b78 100644 --- a/net/tipc/Makefile +++ b/net/tipc/Makefile | |||
| @@ -8,7 +8,7 @@ tipc-y += addr.o bcast.o bearer.o \ | |||
| 8 | core.o link.o discover.o msg.o \ | 8 | core.o link.o discover.o msg.o \ |
| 9 | name_distr.o subscr.o monitor.o name_table.o net.o \ | 9 | name_distr.o subscr.o monitor.o name_table.o net.o \ |
| 10 | netlink.o netlink_compat.o node.o socket.o eth_media.o \ | 10 | netlink.o netlink_compat.o node.o socket.o eth_media.o \ |
| 11 | server.o socket.o | 11 | server.o socket.o group.o |
| 12 | 12 | ||
| 13 | tipc-$(CONFIG_TIPC_MEDIA_UDP) += udp_media.o | 13 | tipc-$(CONFIG_TIPC_MEDIA_UDP) += udp_media.o |
| 14 | tipc-$(CONFIG_TIPC_MEDIA_IB) += ib_media.o | 14 | tipc-$(CONFIG_TIPC_MEDIA_IB) += ib_media.o |
diff --git a/net/tipc/group.c b/net/tipc/group.c new file mode 100644 index 000000000000..3f0e1ce1e3b9 --- /dev/null +++ b/net/tipc/group.c | |||
| @@ -0,0 +1,404 @@ | |||
| 1 | /* | ||
| 2 | * net/tipc/group.c: TIPC group messaging code | ||
| 3 | * | ||
| 4 | * Copyright (c) 2017, Ericsson AB | ||
| 5 | * All rights reserved. | ||
| 6 | * | ||
| 7 | * Redistribution and use in source and binary forms, with or without | ||
| 8 | * modification, are permitted provided that the following conditions are met: | ||
| 9 | * | ||
| 10 | * 1. Redistributions of source code must retain the above copyright | ||
| 11 | * notice, this list of conditions and the following disclaimer. | ||
| 12 | * 2. Redistributions in binary form must reproduce the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer in the | ||
| 14 | * documentation and/or other materials provided with the distribution. | ||
| 15 | * 3. Neither the names of the copyright holders nor the names of its | ||
| 16 | * contributors may be used to endorse or promote products derived from | ||
| 17 | * this software without specific prior written permission. | ||
| 18 | * | ||
| 19 | * Alternatively, this software may be distributed under the terms of the | ||
| 20 | * GNU General Public License ("GPL") version 2 as published by the Free | ||
| 21 | * Software Foundation. | ||
| 22 | * | ||
| 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| 24 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| 25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
| 26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | ||
| 27 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
| 28 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
| 29 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
| 30 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
| 31 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| 32 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| 33 | * POSSIBILITY OF SUCH DAMAGE. | ||
| 34 | */ | ||
| 35 | |||
| 36 | #include "core.h" | ||
| 37 | #include "addr.h" | ||
| 38 | #include "group.h" | ||
| 39 | #include "bcast.h" | ||
| 40 | #include "server.h" | ||
| 41 | #include "msg.h" | ||
| 42 | #include "socket.h" | ||
| 43 | #include "node.h" | ||
| 44 | #include "name_table.h" | ||
| 45 | #include "subscr.h" | ||
| 46 | |||
| 47 | #define ADV_UNIT (((MAX_MSG_SIZE + MAX_H_SIZE) / FLOWCTL_BLK_SZ) + 1) | ||
| 48 | #define ADV_IDLE ADV_UNIT | ||
| 49 | |||
| 50 | enum mbr_state { | ||
| 51 | MBR_QUARANTINED, | ||
| 52 | MBR_DISCOVERED, | ||
| 53 | MBR_JOINING, | ||
| 54 | MBR_PUBLISHED, | ||
| 55 | MBR_JOINED, | ||
| 56 | MBR_LEAVING | ||
| 57 | }; | ||
| 58 | |||
| 59 | struct tipc_member { | ||
| 60 | struct rb_node tree_node; | ||
| 61 | struct list_head list; | ||
| 62 | u32 node; | ||
| 63 | u32 port; | ||
| 64 | enum mbr_state state; | ||
| 65 | u16 bc_rcv_nxt; | ||
| 66 | }; | ||
| 67 | |||
| 68 | struct tipc_group { | ||
| 69 | struct rb_root members; | ||
| 70 | struct tipc_nlist dests; | ||
| 71 | struct net *net; | ||
| 72 | int subid; | ||
| 73 | u32 type; | ||
| 74 | u32 instance; | ||
| 75 | u32 domain; | ||
| 76 | u32 scope; | ||
| 77 | u32 portid; | ||
| 78 | u16 member_cnt; | ||
| 79 | u16 bc_snd_nxt; | ||
| 80 | bool loopback; | ||
| 81 | }; | ||
| 82 | |||
| 83 | static void tipc_group_proto_xmit(struct tipc_group *grp, struct tipc_member *m, | ||
| 84 | int mtyp, struct sk_buff_head *xmitq); | ||
| 85 | |||
| 86 | u16 tipc_group_bc_snd_nxt(struct tipc_group *grp) | ||
| 87 | { | ||
| 88 | return grp->bc_snd_nxt; | ||
| 89 | } | ||
| 90 | |||
| 91 | static bool tipc_group_is_receiver(struct tipc_member *m) | ||
| 92 | { | ||
| 93 | return m && m->state >= MBR_JOINED; | ||
| 94 | } | ||
| 95 | |||
| 96 | int tipc_group_size(struct tipc_group *grp) | ||
| 97 | { | ||
| 98 | return grp->member_cnt; | ||
| 99 | } | ||
| 100 | |||
| 101 | struct tipc_group *tipc_group_create(struct net *net, u32 portid, | ||
| 102 | struct tipc_group_req *mreq) | ||
| 103 | { | ||
| 104 | struct tipc_group *grp; | ||
| 105 | u32 type = mreq->type; | ||
| 106 | |||
| 107 | grp = kzalloc(sizeof(*grp), GFP_ATOMIC); | ||
| 108 | if (!grp) | ||
| 109 | return NULL; | ||
| 110 | tipc_nlist_init(&grp->dests, tipc_own_addr(net)); | ||
| 111 | grp->members = RB_ROOT; | ||
| 112 | grp->net = net; | ||
| 113 | grp->portid = portid; | ||
| 114 | grp->domain = addr_domain(net, mreq->scope); | ||
| 115 | grp->type = type; | ||
| 116 | grp->instance = mreq->instance; | ||
| 117 | grp->scope = mreq->scope; | ||
| 118 | grp->loopback = mreq->flags & TIPC_GROUP_LOOPBACK; | ||
| 119 | if (tipc_topsrv_kern_subscr(net, portid, type, 0, ~0, &grp->subid)) | ||
| 120 | return grp; | ||
| 121 | kfree(grp); | ||
| 122 | return NULL; | ||
| 123 | } | ||
| 124 | |||
| 125 | void tipc_group_delete(struct net *net, struct tipc_group *grp) | ||
| 126 | { | ||
| 127 | struct rb_root *tree = &grp->members; | ||
| 128 | struct tipc_member *m, *tmp; | ||
| 129 | struct sk_buff_head xmitq; | ||
| 130 | |||
| 131 | __skb_queue_head_init(&xmitq); | ||
| 132 | |||
| 133 | rbtree_postorder_for_each_entry_safe(m, tmp, tree, tree_node) { | ||
| 134 | tipc_group_proto_xmit(grp, m, GRP_LEAVE_MSG, &xmitq); | ||
| 135 | list_del(&m->list); | ||
| 136 | kfree(m); | ||
| 137 | } | ||
| 138 | tipc_node_distr_xmit(net, &xmitq); | ||
| 139 | tipc_nlist_purge(&grp->dests); | ||
| 140 | tipc_topsrv_kern_unsubscr(net, grp->subid); | ||
| 141 | kfree(grp); | ||
| 142 | } | ||
| 143 | |||
| 144 | struct tipc_member *tipc_group_find_member(struct tipc_group *grp, | ||
| 145 | u32 node, u32 port) | ||
| 146 | { | ||
| 147 | struct rb_node *n = grp->members.rb_node; | ||
| 148 | u64 nkey, key = (u64)node << 32 | port; | ||
| 149 | struct tipc_member *m; | ||
| 150 | |||
| 151 | while (n) { | ||
| 152 | m = container_of(n, struct tipc_member, tree_node); | ||
| 153 | nkey = (u64)m->node << 32 | m->port; | ||
| 154 | if (key < nkey) | ||
| 155 | n = n->rb_left; | ||
| 156 | else if (key > nkey) | ||
| 157 | n = n->rb_right; | ||
| 158 | else | ||
| 159 | return m; | ||
| 160 | } | ||
| 161 | return NULL; | ||
| 162 | } | ||
| 163 | |||
| 164 | static struct tipc_member *tipc_group_find_node(struct tipc_group *grp, | ||
| 165 | u32 node) | ||
| 166 | { | ||
| 167 | struct tipc_member *m; | ||
| 168 | struct rb_node *n; | ||
| 169 | |||
| 170 | for (n = rb_first(&grp->members); n; n = rb_next(n)) { | ||
| 171 | m = container_of(n, struct tipc_member, tree_node); | ||
| 172 | if (m->node == node) | ||
| 173 | return m; | ||
| 174 | } | ||
| 175 | return NULL; | ||
| 176 | } | ||
| 177 | |||
| 178 | static void tipc_group_add_to_tree(struct tipc_group *grp, | ||
| 179 | struct tipc_member *m) | ||
| 180 | { | ||
| 181 | u64 nkey, key = (u64)m->node << 32 | m->port; | ||
| 182 | struct rb_node **n, *parent = NULL; | ||
| 183 | struct tipc_member *tmp; | ||
| 184 | |||
| 185 | n = &grp->members.rb_node; | ||
| 186 | while (*n) { | ||
| 187 | tmp = container_of(*n, struct tipc_member, tree_node); | ||
| 188 | parent = *n; | ||
| 189 | tmp = container_of(parent, struct tipc_member, tree_node); | ||
| 190 | nkey = (u64)tmp->node << 32 | tmp->port; | ||
| 191 | if (key < nkey) | ||
| 192 | n = &(*n)->rb_left; | ||
| 193 | else if (key > nkey) | ||
| 194 | n = &(*n)->rb_right; | ||
| 195 | else | ||
| 196 | return; | ||
| 197 | } | ||
| 198 | rb_link_node(&m->tree_node, parent, n); | ||
| 199 | rb_insert_color(&m->tree_node, &grp->members); | ||
| 200 | } | ||
| 201 | |||
| 202 | static struct tipc_member *tipc_group_create_member(struct tipc_group *grp, | ||
| 203 | u32 node, u32 port, | ||
| 204 | int state) | ||
| 205 | { | ||
| 206 | struct tipc_member *m; | ||
| 207 | |||
| 208 | m = kzalloc(sizeof(*m), GFP_ATOMIC); | ||
| 209 | if (!m) | ||
| 210 | return NULL; | ||
| 211 | INIT_LIST_HEAD(&m->list); | ||
| 212 | m->node = node; | ||
| 213 | m->port = port; | ||
| 214 | grp->member_cnt++; | ||
| 215 | tipc_group_add_to_tree(grp, m); | ||
| 216 | tipc_nlist_add(&grp->dests, m->node); | ||
| 217 | m->state = state; | ||
| 218 | return m; | ||
| 219 | } | ||
| 220 | |||
| 221 | void tipc_group_add_member(struct tipc_group *grp, u32 node, u32 port) | ||
| 222 | { | ||
| 223 | tipc_group_create_member(grp, node, port, MBR_DISCOVERED); | ||
| 224 | } | ||
| 225 | |||
| 226 | static void tipc_group_delete_member(struct tipc_group *grp, | ||
| 227 | struct tipc_member *m) | ||
| 228 | { | ||
| 229 | rb_erase(&m->tree_node, &grp->members); | ||
| 230 | grp->member_cnt--; | ||
| 231 | list_del_init(&m->list); | ||
| 232 | |||
| 233 | /* If last member on a node, remove node from dest list */ | ||
| 234 | if (!tipc_group_find_node(grp, m->node)) | ||
| 235 | tipc_nlist_del(&grp->dests, m->node); | ||
| 236 | |||
| 237 | kfree(m); | ||
| 238 | } | ||
| 239 | |||
| 240 | struct tipc_nlist *tipc_group_dests(struct tipc_group *grp) | ||
| 241 | { | ||
| 242 | return &grp->dests; | ||
| 243 | } | ||
| 244 | |||
| 245 | void tipc_group_self(struct tipc_group *grp, struct tipc_name_seq *seq, | ||
| 246 | int *scope) | ||
| 247 | { | ||
| 248 | seq->type = grp->type; | ||
| 249 | seq->lower = grp->instance; | ||
| 250 | seq->upper = grp->instance; | ||
| 251 | *scope = grp->scope; | ||
| 252 | } | ||
| 253 | |||
| 254 | void tipc_group_update_bc_members(struct tipc_group *grp) | ||
| 255 | { | ||
| 256 | grp->bc_snd_nxt++; | ||
| 257 | } | ||
| 258 | |||
| 259 | /* tipc_group_filter_msg() - determine if we should accept arriving message | ||
| 260 | */ | ||
| 261 | void tipc_group_filter_msg(struct tipc_group *grp, struct sk_buff_head *inputq, | ||
| 262 | struct sk_buff_head *xmitq) | ||
| 263 | { | ||
| 264 | struct sk_buff *skb = __skb_dequeue(inputq); | ||
| 265 | struct tipc_member *m; | ||
| 266 | struct tipc_msg *hdr; | ||
| 267 | u32 node, port; | ||
| 268 | int mtyp; | ||
| 269 | |||
| 270 | if (!skb) | ||
| 271 | return; | ||
| 272 | |||
| 273 | hdr = buf_msg(skb); | ||
| 274 | mtyp = msg_type(hdr); | ||
| 275 | node = msg_orignode(hdr); | ||
| 276 | port = msg_origport(hdr); | ||
| 277 | |||
| 278 | if (!msg_in_group(hdr)) | ||
| 279 | goto drop; | ||
| 280 | |||
| 281 | m = tipc_group_find_member(grp, node, port); | ||
| 282 | if (!tipc_group_is_receiver(m)) | ||
| 283 | goto drop; | ||
| 284 | |||
| 285 | __skb_queue_tail(inputq, skb); | ||
| 286 | |||
| 287 | m->bc_rcv_nxt = msg_grp_bc_seqno(hdr) + 1; | ||
| 288 | return; | ||
| 289 | drop: | ||
| 290 | kfree_skb(skb); | ||
| 291 | } | ||
| 292 | |||
| 293 | static void tipc_group_proto_xmit(struct tipc_group *grp, struct tipc_member *m, | ||
| 294 | int mtyp, struct sk_buff_head *xmitq) | ||
| 295 | { | ||
| 296 | struct tipc_msg *hdr; | ||
| 297 | struct sk_buff *skb; | ||
| 298 | |||
| 299 | skb = tipc_msg_create(GROUP_PROTOCOL, mtyp, INT_H_SIZE, 0, | ||
| 300 | m->node, tipc_own_addr(grp->net), | ||
| 301 | m->port, grp->portid, 0); | ||
| 302 | if (!skb) | ||
| 303 | return; | ||
| 304 | |||
| 305 | hdr = buf_msg(skb); | ||
| 306 | if (mtyp == GRP_JOIN_MSG) | ||
| 307 | msg_set_grp_bc_syncpt(hdr, grp->bc_snd_nxt); | ||
| 308 | __skb_queue_tail(xmitq, skb); | ||
| 309 | } | ||
| 310 | |||
| 311 | void tipc_group_proto_rcv(struct tipc_group *grp, struct tipc_msg *hdr, | ||
| 312 | struct sk_buff_head *xmitq) | ||
| 313 | { | ||
| 314 | u32 node = msg_orignode(hdr); | ||
| 315 | u32 port = msg_origport(hdr); | ||
| 316 | struct tipc_member *m; | ||
| 317 | |||
| 318 | if (!grp) | ||
| 319 | return; | ||
| 320 | |||
| 321 | m = tipc_group_find_member(grp, node, port); | ||
| 322 | |||
| 323 | switch (msg_type(hdr)) { | ||
| 324 | case GRP_JOIN_MSG: | ||
| 325 | if (!m) | ||
| 326 | m = tipc_group_create_member(grp, node, port, | ||
| 327 | MBR_QUARANTINED); | ||
| 328 | if (!m) | ||
| 329 | return; | ||
| 330 | m->bc_rcv_nxt = msg_grp_bc_syncpt(hdr); | ||
| 331 | |||
| 332 | /* Wait until PUBLISH event is received */ | ||
| 333 | if (m->state == MBR_DISCOVERED) | ||
| 334 | m->state = MBR_JOINING; | ||
| 335 | else if (m->state == MBR_PUBLISHED) | ||
| 336 | m->state = MBR_JOINED; | ||
| 337 | return; | ||
| 338 | case GRP_LEAVE_MSG: | ||
| 339 | if (!m) | ||
| 340 | return; | ||
| 341 | |||
| 342 | /* Wait until WITHDRAW event is received */ | ||
| 343 | if (m->state != MBR_LEAVING) { | ||
| 344 | m->state = MBR_LEAVING; | ||
| 345 | return; | ||
| 346 | } | ||
| 347 | /* Otherwise deliver already received WITHDRAW event */ | ||
| 348 | tipc_group_delete_member(grp, m); | ||
| 349 | return; | ||
| 350 | default: | ||
| 351 | pr_warn("Received unknown GROUP_PROTO message\n"); | ||
| 352 | } | ||
| 353 | } | ||
| 354 | |||
| 355 | /* tipc_group_member_evt() - receive and handle a member up/down event | ||
| 356 | */ | ||
| 357 | void tipc_group_member_evt(struct tipc_group *grp, | ||
| 358 | struct sk_buff *skb, | ||
| 359 | struct sk_buff_head *xmitq) | ||
| 360 | { | ||
| 361 | struct tipc_msg *hdr = buf_msg(skb); | ||
| 362 | struct tipc_event *evt = (void *)msg_data(hdr); | ||
| 363 | u32 node = evt->port.node; | ||
| 364 | u32 port = evt->port.ref; | ||
| 365 | struct tipc_member *m; | ||
| 366 | struct net *net; | ||
| 367 | u32 self; | ||
| 368 | |||
| 369 | if (!grp) | ||
| 370 | goto drop; | ||
| 371 | |||
| 372 | net = grp->net; | ||
| 373 | self = tipc_own_addr(net); | ||
| 374 | if (!grp->loopback && node == self && port == grp->portid) | ||
| 375 | goto drop; | ||
| 376 | |||
| 377 | m = tipc_group_find_member(grp, node, port); | ||
| 378 | |||
| 379 | if (evt->event == TIPC_PUBLISHED) { | ||
| 380 | if (!m) | ||
| 381 | m = tipc_group_create_member(grp, node, port, | ||
| 382 | MBR_DISCOVERED); | ||
| 383 | if (!m) | ||
| 384 | goto drop; | ||
| 385 | |||
| 386 | /* Wait if JOIN message not yet received */ | ||
| 387 | if (m->state == MBR_DISCOVERED) | ||
| 388 | m->state = MBR_PUBLISHED; | ||
| 389 | else | ||
| 390 | m->state = MBR_JOINED; | ||
| 391 | tipc_group_proto_xmit(grp, m, GRP_JOIN_MSG, xmitq); | ||
| 392 | } else if (evt->event == TIPC_WITHDRAWN) { | ||
| 393 | if (!m) | ||
| 394 | goto drop; | ||
| 395 | |||
| 396 | /* Keep back event if more messages might be expected */ | ||
| 397 | if (m->state != MBR_LEAVING && tipc_node_is_up(net, node)) | ||
| 398 | m->state = MBR_LEAVING; | ||
| 399 | else | ||
| 400 | tipc_group_delete_member(grp, m); | ||
| 401 | } | ||
| 402 | drop: | ||
| 403 | kfree_skb(skb); | ||
| 404 | } | ||
diff --git a/net/tipc/group.h b/net/tipc/group.h new file mode 100644 index 000000000000..9bdf4479fc03 --- /dev/null +++ b/net/tipc/group.h | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | /* | ||
| 2 | * net/tipc/group.h: Include file for TIPC group unicast/multicast functions | ||
| 3 | * | ||
| 4 | * Copyright (c) 2017, Ericsson AB | ||
| 5 | * All rights reserved. | ||
| 6 | * | ||
| 7 | * Redistribution and use in source and binary forms, with or without | ||
| 8 | * modification, are permitted provided that the following conditions are met: | ||
| 9 | * | ||
| 10 | * 1. Redistributions of source code must retain the above copyright | ||
| 11 | * notice, this list of conditions and the following disclaimer. | ||
| 12 | * 2. Redistributions in binary form must reproduce the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer in the | ||
| 14 | * documentation and/or other materials provided with the distribution. | ||
| 15 | * 3. Neither the names of the copyright holders nor the names of its | ||
| 16 | * contributors may be used to endorse or promote products derived from | ||
| 17 | * this software without specific prior written permission. | ||
| 18 | * | ||
| 19 | * Alternatively, this software may be distributed under the terms of the | ||
| 20 | * GNU General Public License ("GPL") version 2 as published by the Free | ||
| 21 | * Software Foundation. | ||
| 22 | * | ||
| 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| 24 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| 25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
| 26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | ||
| 27 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
| 28 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
| 29 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
| 30 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
| 31 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| 32 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| 33 | * POSSIBILITY OF SUCH DAMAGE. | ||
| 34 | */ | ||
| 35 | |||
| 36 | #ifndef _TIPC_GROUP_H | ||
| 37 | #define _TIPC_GROUP_H | ||
| 38 | |||
| 39 | #include "core.h" | ||
| 40 | |||
| 41 | struct tipc_group; | ||
| 42 | struct tipc_member; | ||
| 43 | struct tipc_msg; | ||
| 44 | |||
| 45 | struct tipc_group *tipc_group_create(struct net *net, u32 portid, | ||
| 46 | struct tipc_group_req *mreq); | ||
| 47 | void tipc_group_delete(struct net *net, struct tipc_group *grp); | ||
| 48 | void tipc_group_add_member(struct tipc_group *grp, u32 node, u32 port); | ||
| 49 | struct tipc_nlist *tipc_group_dests(struct tipc_group *grp); | ||
| 50 | void tipc_group_self(struct tipc_group *grp, struct tipc_name_seq *seq, | ||
| 51 | int *scope); | ||
| 52 | void tipc_group_filter_msg(struct tipc_group *grp, | ||
| 53 | struct sk_buff_head *inputq, | ||
| 54 | struct sk_buff_head *xmitq); | ||
| 55 | void tipc_group_member_evt(struct tipc_group *grp, | ||
| 56 | struct sk_buff *skb, | ||
| 57 | struct sk_buff_head *xmitq); | ||
| 58 | void tipc_group_proto_rcv(struct tipc_group *grp, | ||
| 59 | struct tipc_msg *hdr, | ||
| 60 | struct sk_buff_head *xmitq); | ||
| 61 | void tipc_group_update_bc_members(struct tipc_group *grp); | ||
| 62 | u16 tipc_group_bc_snd_nxt(struct tipc_group *grp); | ||
| 63 | int tipc_group_size(struct tipc_group *grp); | ||
| 64 | #endif | ||
diff --git a/net/tipc/link.c b/net/tipc/link.c index ac0144f532aa..bd25bff63925 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c | |||
| @@ -1046,11 +1046,12 @@ static bool tipc_data_input(struct tipc_link *l, struct sk_buff *skb, | |||
| 1046 | case TIPC_MEDIUM_IMPORTANCE: | 1046 | case TIPC_MEDIUM_IMPORTANCE: |
| 1047 | case TIPC_HIGH_IMPORTANCE: | 1047 | case TIPC_HIGH_IMPORTANCE: |
| 1048 | case TIPC_CRITICAL_IMPORTANCE: | 1048 | case TIPC_CRITICAL_IMPORTANCE: |
| 1049 | if (unlikely(msg_type(hdr) == TIPC_MCAST_MSG)) { | 1049 | if (unlikely(msg_mcast(hdr))) { |
| 1050 | skb_queue_tail(l->bc_rcvlink->inputq, skb); | 1050 | skb_queue_tail(l->bc_rcvlink->inputq, skb); |
| 1051 | return true; | 1051 | return true; |
| 1052 | } | 1052 | } |
| 1053 | case CONN_MANAGER: | 1053 | case CONN_MANAGER: |
| 1054 | case GROUP_PROTOCOL: | ||
| 1054 | skb_queue_tail(inputq, skb); | 1055 | skb_queue_tail(inputq, skb); |
| 1055 | return true; | 1056 | return true; |
| 1056 | case NAME_DISTRIBUTOR: | 1057 | case NAME_DISTRIBUTOR: |
diff --git a/net/tipc/msg.h b/net/tipc/msg.h index be3e38aa9dd2..dad400935405 100644 --- a/net/tipc/msg.h +++ b/net/tipc/msg.h | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * net/tipc/msg.h: Include file for TIPC message header routines | 2 | * net/tipc/msg.h: Include file for TIPC message header routines |
| 3 | * | 3 | * |
| 4 | * Copyright (c) 2000-2007, 2014-2015 Ericsson AB | 4 | * Copyright (c) 2000-2007, 2014-2017 Ericsson AB |
| 5 | * Copyright (c) 2005-2008, 2010-2011, Wind River Systems | 5 | * Copyright (c) 2005-2008, 2010-2011, Wind River Systems |
| 6 | * All rights reserved. | 6 | * All rights reserved. |
| 7 | * | 7 | * |
| @@ -61,10 +61,11 @@ struct plist; | |||
| 61 | /* | 61 | /* |
| 62 | * Payload message types | 62 | * Payload message types |
| 63 | */ | 63 | */ |
| 64 | #define TIPC_CONN_MSG 0 | 64 | #define TIPC_CONN_MSG 0 |
| 65 | #define TIPC_MCAST_MSG 1 | 65 | #define TIPC_MCAST_MSG 1 |
| 66 | #define TIPC_NAMED_MSG 2 | 66 | #define TIPC_NAMED_MSG 2 |
| 67 | #define TIPC_DIRECT_MSG 3 | 67 | #define TIPC_DIRECT_MSG 3 |
| 68 | #define TIPC_GRP_BCAST_MSG 4 | ||
| 68 | 69 | ||
| 69 | /* | 70 | /* |
| 70 | * Internal message users | 71 | * Internal message users |
| @@ -73,6 +74,7 @@ struct plist; | |||
| 73 | #define MSG_BUNDLER 6 | 74 | #define MSG_BUNDLER 6 |
| 74 | #define LINK_PROTOCOL 7 | 75 | #define LINK_PROTOCOL 7 |
| 75 | #define CONN_MANAGER 8 | 76 | #define CONN_MANAGER 8 |
| 77 | #define GROUP_PROTOCOL 9 | ||
| 76 | #define TUNNEL_PROTOCOL 10 | 78 | #define TUNNEL_PROTOCOL 10 |
| 77 | #define NAME_DISTRIBUTOR 11 | 79 | #define NAME_DISTRIBUTOR 11 |
| 78 | #define MSG_FRAGMENTER 12 | 80 | #define MSG_FRAGMENTER 12 |
| @@ -87,6 +89,7 @@ struct plist; | |||
| 87 | #define BASIC_H_SIZE 32 /* Basic payload message */ | 89 | #define BASIC_H_SIZE 32 /* Basic payload message */ |
| 88 | #define NAMED_H_SIZE 40 /* Named payload message */ | 90 | #define NAMED_H_SIZE 40 /* Named payload message */ |
| 89 | #define MCAST_H_SIZE 44 /* Multicast payload message */ | 91 | #define MCAST_H_SIZE 44 /* Multicast payload message */ |
| 92 | #define GROUP_H_SIZE 44 /* Group payload message */ | ||
| 90 | #define INT_H_SIZE 40 /* Internal messages */ | 93 | #define INT_H_SIZE 40 /* Internal messages */ |
| 91 | #define MIN_H_SIZE 24 /* Smallest legal TIPC header size */ | 94 | #define MIN_H_SIZE 24 /* Smallest legal TIPC header size */ |
| 92 | #define MAX_H_SIZE 60 /* Largest possible TIPC header size */ | 95 | #define MAX_H_SIZE 60 /* Largest possible TIPC header size */ |
| @@ -252,6 +255,11 @@ static inline void msg_set_type(struct tipc_msg *m, u32 n) | |||
| 252 | msg_set_bits(m, 1, 29, 0x7, n); | 255 | msg_set_bits(m, 1, 29, 0x7, n); |
| 253 | } | 256 | } |
| 254 | 257 | ||
| 258 | static inline int msg_in_group(struct tipc_msg *m) | ||
| 259 | { | ||
| 260 | return (msg_type(m) == TIPC_GRP_BCAST_MSG); | ||
| 261 | } | ||
| 262 | |||
| 255 | static inline u32 msg_named(struct tipc_msg *m) | 263 | static inline u32 msg_named(struct tipc_msg *m) |
| 256 | { | 264 | { |
| 257 | return msg_type(m) == TIPC_NAMED_MSG; | 265 | return msg_type(m) == TIPC_NAMED_MSG; |
| @@ -259,7 +267,9 @@ static inline u32 msg_named(struct tipc_msg *m) | |||
| 259 | 267 | ||
| 260 | static inline u32 msg_mcast(struct tipc_msg *m) | 268 | static inline u32 msg_mcast(struct tipc_msg *m) |
| 261 | { | 269 | { |
| 262 | return msg_type(m) == TIPC_MCAST_MSG; | 270 | int mtyp = msg_type(m); |
| 271 | |||
| 272 | return ((mtyp == TIPC_MCAST_MSG) || (mtyp == TIPC_GRP_BCAST_MSG)); | ||
| 263 | } | 273 | } |
| 264 | 274 | ||
| 265 | static inline u32 msg_connected(struct tipc_msg *m) | 275 | static inline u32 msg_connected(struct tipc_msg *m) |
| @@ -515,6 +525,12 @@ static inline void msg_set_nameupper(struct tipc_msg *m, u32 n) | |||
| 515 | #define DSC_RESP_MSG 1 | 525 | #define DSC_RESP_MSG 1 |
| 516 | 526 | ||
| 517 | /* | 527 | /* |
| 528 | * Group protocol message types | ||
| 529 | */ | ||
| 530 | #define GRP_JOIN_MSG 0 | ||
| 531 | #define GRP_LEAVE_MSG 1 | ||
| 532 | |||
| 533 | /* | ||
| 518 | * Word 1 | 534 | * Word 1 |
| 519 | */ | 535 | */ |
| 520 | static inline u32 msg_seq_gap(struct tipc_msg *m) | 536 | static inline u32 msg_seq_gap(struct tipc_msg *m) |
| @@ -795,6 +811,28 @@ static inline void msg_set_link_tolerance(struct tipc_msg *m, u32 n) | |||
| 795 | msg_set_bits(m, 9, 0, 0xffff, n); | 811 | msg_set_bits(m, 9, 0, 0xffff, n); |
| 796 | } | 812 | } |
| 797 | 813 | ||
| 814 | static inline u16 msg_grp_bc_syncpt(struct tipc_msg *m) | ||
| 815 | { | ||
| 816 | return msg_bits(m, 9, 16, 0xffff); | ||
| 817 | } | ||
| 818 | |||
| 819 | static inline void msg_set_grp_bc_syncpt(struct tipc_msg *m, u16 n) | ||
| 820 | { | ||
| 821 | msg_set_bits(m, 9, 16, 0xffff, n); | ||
| 822 | } | ||
| 823 | |||
| 824 | /* Word 10 | ||
| 825 | */ | ||
| 826 | static inline u16 msg_grp_bc_seqno(struct tipc_msg *m) | ||
| 827 | { | ||
| 828 | return msg_bits(m, 10, 16, 0xffff); | ||
| 829 | } | ||
| 830 | |||
| 831 | static inline void msg_set_grp_bc_seqno(struct tipc_msg *m, u32 n) | ||
| 832 | { | ||
| 833 | msg_set_bits(m, 10, 16, 0xffff, n); | ||
| 834 | } | ||
| 835 | |||
| 798 | static inline bool msg_peer_link_is_up(struct tipc_msg *m) | 836 | static inline bool msg_peer_link_is_up(struct tipc_msg *m) |
| 799 | { | 837 | { |
| 800 | if (likely(msg_user(m) != LINK_PROTOCOL)) | 838 | if (likely(msg_user(m) != LINK_PROTOCOL)) |
diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c index 76bd2777baaf..114d72bab827 100644 --- a/net/tipc/name_table.c +++ b/net/tipc/name_table.c | |||
| @@ -43,6 +43,7 @@ | |||
| 43 | #include "bcast.h" | 43 | #include "bcast.h" |
| 44 | #include "addr.h" | 44 | #include "addr.h" |
| 45 | #include "node.h" | 45 | #include "node.h" |
| 46 | #include "group.h" | ||
| 46 | #include <net/genetlink.h> | 47 | #include <net/genetlink.h> |
| 47 | 48 | ||
| 48 | #define TIPC_NAMETBL_SIZE 1024 /* must be a power of 2 */ | 49 | #define TIPC_NAMETBL_SIZE 1024 /* must be a power of 2 */ |
| @@ -596,18 +597,6 @@ not_found: | |||
| 596 | return ref; | 597 | return ref; |
| 597 | } | 598 | } |
| 598 | 599 | ||
| 599 | /** | ||
| 600 | * tipc_nametbl_mc_translate - find multicast destinations | ||
| 601 | * | ||
| 602 | * Creates list of all local ports that overlap the given multicast address; | ||
| 603 | * also determines if any off-node ports overlap. | ||
| 604 | * | ||
| 605 | * Note: Publications with a scope narrower than 'limit' are ignored. | ||
| 606 | * (i.e. local node-scope publications mustn't receive messages arriving | ||
| 607 | * from another node, even if the multcast link brought it here) | ||
| 608 | * | ||
| 609 | * Returns non-zero if any off-node ports overlap | ||
| 610 | */ | ||
| 611 | int tipc_nametbl_mc_translate(struct net *net, u32 type, u32 lower, u32 upper, | 600 | int tipc_nametbl_mc_translate(struct net *net, u32 type, u32 lower, u32 upper, |
| 612 | u32 limit, struct list_head *dports) | 601 | u32 limit, struct list_head *dports) |
| 613 | { | 602 | { |
| @@ -679,6 +668,37 @@ exit: | |||
| 679 | rcu_read_unlock(); | 668 | rcu_read_unlock(); |
| 680 | } | 669 | } |
| 681 | 670 | ||
| 671 | /* tipc_nametbl_build_group - build list of communication group members | ||
| 672 | */ | ||
| 673 | void tipc_nametbl_build_group(struct net *net, struct tipc_group *grp, | ||
| 674 | u32 type, u32 domain) | ||
| 675 | { | ||
| 676 | struct sub_seq *sseq, *stop; | ||
| 677 | struct name_info *info; | ||
| 678 | struct publication *p; | ||
| 679 | struct name_seq *seq; | ||
| 680 | |||
| 681 | rcu_read_lock(); | ||
| 682 | seq = nametbl_find_seq(net, type); | ||
| 683 | if (!seq) | ||
| 684 | goto exit; | ||
| 685 | |||
| 686 | spin_lock_bh(&seq->lock); | ||
| 687 | sseq = seq->sseqs; | ||
| 688 | stop = seq->sseqs + seq->first_free; | ||
| 689 | for (; sseq != stop; sseq++) { | ||
| 690 | info = sseq->info; | ||
| 691 | list_for_each_entry(p, &info->zone_list, zone_list) { | ||
| 692 | if (!tipc_in_scope(domain, p->node)) | ||
| 693 | continue; | ||
| 694 | tipc_group_add_member(grp, p->node, p->ref); | ||
| 695 | } | ||
| 696 | } | ||
| 697 | spin_unlock_bh(&seq->lock); | ||
| 698 | exit: | ||
| 699 | rcu_read_unlock(); | ||
| 700 | } | ||
| 701 | |||
| 682 | /* | 702 | /* |
| 683 | * tipc_nametbl_publish - add name publication to network name tables | 703 | * tipc_nametbl_publish - add name publication to network name tables |
| 684 | */ | 704 | */ |
diff --git a/net/tipc/name_table.h b/net/tipc/name_table.h index d121175a92b5..97646b17a4a2 100644 --- a/net/tipc/name_table.h +++ b/net/tipc/name_table.h | |||
| @@ -40,6 +40,7 @@ | |||
| 40 | struct tipc_subscription; | 40 | struct tipc_subscription; |
| 41 | struct tipc_plist; | 41 | struct tipc_plist; |
| 42 | struct tipc_nlist; | 42 | struct tipc_nlist; |
| 43 | struct tipc_group; | ||
| 43 | 44 | ||
| 44 | /* | 45 | /* |
| 45 | * TIPC name types reserved for internal TIPC use (both current and planned) | 46 | * TIPC name types reserved for internal TIPC use (both current and planned) |
| @@ -101,6 +102,8 @@ int tipc_nl_name_table_dump(struct sk_buff *skb, struct netlink_callback *cb); | |||
| 101 | u32 tipc_nametbl_translate(struct net *net, u32 type, u32 instance, u32 *node); | 102 | u32 tipc_nametbl_translate(struct net *net, u32 type, u32 instance, u32 *node); |
| 102 | int tipc_nametbl_mc_translate(struct net *net, u32 type, u32 lower, u32 upper, | 103 | int tipc_nametbl_mc_translate(struct net *net, u32 type, u32 lower, u32 upper, |
| 103 | u32 limit, struct list_head *dports); | 104 | u32 limit, struct list_head *dports); |
| 105 | void tipc_nametbl_build_group(struct net *net, struct tipc_group *grp, | ||
| 106 | u32 type, u32 domain); | ||
| 104 | void tipc_nametbl_lookup_dst_nodes(struct net *net, u32 type, u32 lower, | 107 | void tipc_nametbl_lookup_dst_nodes(struct net *net, u32 type, u32 lower, |
| 105 | u32 upper, u32 domain, | 108 | u32 upper, u32 domain, |
| 106 | struct tipc_nlist *nodes); | 109 | struct tipc_nlist *nodes); |
diff --git a/net/tipc/node.h b/net/tipc/node.h index df2f2197c4ad..acd58d23a70e 100644 --- a/net/tipc/node.h +++ b/net/tipc/node.h | |||
| @@ -48,7 +48,8 @@ enum { | |||
| 48 | TIPC_BCAST_SYNCH = (1 << 1), | 48 | TIPC_BCAST_SYNCH = (1 << 1), |
| 49 | TIPC_BCAST_STATE_NACK = (1 << 2), | 49 | TIPC_BCAST_STATE_NACK = (1 << 2), |
| 50 | TIPC_BLOCK_FLOWCTL = (1 << 3), | 50 | TIPC_BLOCK_FLOWCTL = (1 << 3), |
| 51 | TIPC_BCAST_RCAST = (1 << 4) | 51 | TIPC_BCAST_RCAST = (1 << 4), |
| 52 | TIPC_MCAST_GROUPS = (1 << 5) | ||
| 52 | }; | 53 | }; |
| 53 | 54 | ||
| 54 | #define TIPC_NODE_CAPABILITIES (TIPC_BCAST_SYNCH | \ | 55 | #define TIPC_NODE_CAPABILITIES (TIPC_BCAST_SYNCH | \ |
diff --git a/net/tipc/socket.c b/net/tipc/socket.c index daf7c4df4531..64bbf9d03629 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * net/tipc/socket.c: TIPC socket API | 2 | * net/tipc/socket.c: TIPC socket API |
| 3 | * | 3 | * |
| 4 | * Copyright (c) 2001-2007, 2012-2016, Ericsson AB | 4 | * Copyright (c) 2001-2007, 2012-2017, Ericsson AB |
| 5 | * Copyright (c) 2004-2008, 2010-2013, Wind River Systems | 5 | * Copyright (c) 2004-2008, 2010-2013, Wind River Systems |
| 6 | * All rights reserved. | 6 | * All rights reserved. |
| 7 | * | 7 | * |
| @@ -45,6 +45,7 @@ | |||
| 45 | #include "socket.h" | 45 | #include "socket.h" |
| 46 | #include "bcast.h" | 46 | #include "bcast.h" |
| 47 | #include "netlink.h" | 47 | #include "netlink.h" |
| 48 | #include "group.h" | ||
| 48 | 49 | ||
| 49 | #define CONN_TIMEOUT_DEFAULT 8000 /* default connect timeout = 8s */ | 50 | #define CONN_TIMEOUT_DEFAULT 8000 /* default connect timeout = 8s */ |
| 50 | #define CONN_PROBING_INTERVAL msecs_to_jiffies(3600000) /* [ms] => 1 h */ | 51 | #define CONN_PROBING_INTERVAL msecs_to_jiffies(3600000) /* [ms] => 1 h */ |
| @@ -78,7 +79,7 @@ enum { | |||
| 78 | * @conn_timeout: the time we can wait for an unresponded setup request | 79 | * @conn_timeout: the time we can wait for an unresponded setup request |
| 79 | * @dupl_rcvcnt: number of bytes counted twice, in both backlog and rcv queue | 80 | * @dupl_rcvcnt: number of bytes counted twice, in both backlog and rcv queue |
| 80 | * @cong_link_cnt: number of congested links | 81 | * @cong_link_cnt: number of congested links |
| 81 | * @sent_unacked: # messages sent by socket, and not yet acked by peer | 82 | * @snt_unacked: # messages sent by socket, and not yet acked by peer |
| 82 | * @rcv_unacked: # messages read by user, but not yet acked back to peer | 83 | * @rcv_unacked: # messages read by user, but not yet acked back to peer |
| 83 | * @peer: 'connected' peer for dgram/rdm | 84 | * @peer: 'connected' peer for dgram/rdm |
| 84 | * @node: hash table node | 85 | * @node: hash table node |
| @@ -109,6 +110,7 @@ struct tipc_sock { | |||
| 109 | struct rhash_head node; | 110 | struct rhash_head node; |
| 110 | struct tipc_mc_method mc_method; | 111 | struct tipc_mc_method mc_method; |
| 111 | struct rcu_head rcu; | 112 | struct rcu_head rcu; |
| 113 | struct tipc_group *group; | ||
| 112 | }; | 114 | }; |
| 113 | 115 | ||
| 114 | static int tipc_sk_backlog_rcv(struct sock *sk, struct sk_buff *skb); | 116 | static int tipc_sk_backlog_rcv(struct sock *sk, struct sk_buff *skb); |
| @@ -123,6 +125,7 @@ static int tipc_sk_publish(struct tipc_sock *tsk, uint scope, | |||
| 123 | struct tipc_name_seq const *seq); | 125 | struct tipc_name_seq const *seq); |
| 124 | static int tipc_sk_withdraw(struct tipc_sock *tsk, uint scope, | 126 | static int tipc_sk_withdraw(struct tipc_sock *tsk, uint scope, |
| 125 | struct tipc_name_seq const *seq); | 127 | struct tipc_name_seq const *seq); |
| 128 | static int tipc_sk_leave(struct tipc_sock *tsk); | ||
| 126 | static struct tipc_sock *tipc_sk_lookup(struct net *net, u32 portid); | 129 | static struct tipc_sock *tipc_sk_lookup(struct net *net, u32 portid); |
| 127 | static int tipc_sk_insert(struct tipc_sock *tsk); | 130 | static int tipc_sk_insert(struct tipc_sock *tsk); |
| 128 | static void tipc_sk_remove(struct tipc_sock *tsk); | 131 | static void tipc_sk_remove(struct tipc_sock *tsk); |
| @@ -559,6 +562,7 @@ static int tipc_release(struct socket *sock) | |||
| 559 | 562 | ||
| 560 | __tipc_shutdown(sock, TIPC_ERR_NO_PORT); | 563 | __tipc_shutdown(sock, TIPC_ERR_NO_PORT); |
| 561 | sk->sk_shutdown = SHUTDOWN_MASK; | 564 | sk->sk_shutdown = SHUTDOWN_MASK; |
| 565 | tipc_sk_leave(tsk); | ||
| 562 | tipc_sk_withdraw(tsk, 0, NULL); | 566 | tipc_sk_withdraw(tsk, 0, NULL); |
| 563 | sk_stop_timer(sk, &sk->sk_timer); | 567 | sk_stop_timer(sk, &sk->sk_timer); |
| 564 | tipc_sk_remove(tsk); | 568 | tipc_sk_remove(tsk); |
| @@ -601,7 +605,10 @@ static int tipc_bind(struct socket *sock, struct sockaddr *uaddr, | |||
| 601 | res = tipc_sk_withdraw(tsk, 0, NULL); | 605 | res = tipc_sk_withdraw(tsk, 0, NULL); |
| 602 | goto exit; | 606 | goto exit; |
| 603 | } | 607 | } |
| 604 | 608 | if (tsk->group) { | |
| 609 | res = -EACCES; | ||
| 610 | goto exit; | ||
| 611 | } | ||
| 605 | if (uaddr_len < sizeof(struct sockaddr_tipc)) { | 612 | if (uaddr_len < sizeof(struct sockaddr_tipc)) { |
| 606 | res = -EINVAL; | 613 | res = -EINVAL; |
| 607 | goto exit; | 614 | goto exit; |
| @@ -698,6 +705,7 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock, | |||
| 698 | { | 705 | { |
| 699 | struct sock *sk = sock->sk; | 706 | struct sock *sk = sock->sk; |
| 700 | struct tipc_sock *tsk = tipc_sk(sk); | 707 | struct tipc_sock *tsk = tipc_sk(sk); |
| 708 | struct tipc_group *grp = tsk->group; | ||
| 701 | u32 mask = 0; | 709 | u32 mask = 0; |
| 702 | 710 | ||
| 703 | sock_poll_wait(file, sk_sleep(sk), wait); | 711 | sock_poll_wait(file, sk_sleep(sk), wait); |
| @@ -718,8 +726,9 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock, | |||
| 718 | mask |= (POLLIN | POLLRDNORM); | 726 | mask |= (POLLIN | POLLRDNORM); |
| 719 | break; | 727 | break; |
| 720 | case TIPC_OPEN: | 728 | case TIPC_OPEN: |
| 721 | if (!tsk->cong_link_cnt) | 729 | if (!grp || tipc_group_size(grp)) |
| 722 | mask |= POLLOUT; | 730 | if (!tsk->cong_link_cnt) |
| 731 | mask |= POLLOUT; | ||
| 723 | if (tipc_sk_type_connectionless(sk) && | 732 | if (tipc_sk_type_connectionless(sk) && |
| 724 | (!skb_queue_empty(&sk->sk_receive_queue))) | 733 | (!skb_queue_empty(&sk->sk_receive_queue))) |
| 725 | mask |= (POLLIN | POLLRDNORM); | 734 | mask |= (POLLIN | POLLRDNORM); |
| @@ -757,6 +766,9 @@ static int tipc_sendmcast(struct socket *sock, struct tipc_name_seq *seq, | |||
| 757 | struct tipc_nlist dsts; | 766 | struct tipc_nlist dsts; |
| 758 | int rc; | 767 | int rc; |
| 759 | 768 | ||
| 769 | if (tsk->group) | ||
| 770 | return -EACCES; | ||
| 771 | |||
| 760 | /* Block or return if any destination link is congested */ | 772 | /* Block or return if any destination link is congested */ |
| 761 | rc = tipc_wait_for_cond(sock, &timeout, !tsk->cong_link_cnt); | 773 | rc = tipc_wait_for_cond(sock, &timeout, !tsk->cong_link_cnt); |
| 762 | if (unlikely(rc)) | 774 | if (unlikely(rc)) |
| @@ -794,6 +806,64 @@ static int tipc_sendmcast(struct socket *sock, struct tipc_name_seq *seq, | |||
| 794 | } | 806 | } |
| 795 | 807 | ||
| 796 | /** | 808 | /** |
| 809 | * tipc_send_group_bcast - send message to all members in communication group | ||
| 810 | * @sk: socket structure | ||
| 811 | * @m: message to send | ||
| 812 | * @dlen: total length of message data | ||
| 813 | * @timeout: timeout to wait for wakeup | ||
| 814 | * | ||
| 815 | * Called from function tipc_sendmsg(), which has done all sanity checks | ||
| 816 | * Returns the number of bytes sent on success, or errno | ||
| 817 | */ | ||
| 818 | static int tipc_send_group_bcast(struct socket *sock, struct msghdr *m, | ||
| 819 | int dlen, long timeout) | ||
| 820 | { | ||
| 821 | struct sock *sk = sock->sk; | ||
| 822 | struct net *net = sock_net(sk); | ||
| 823 | struct tipc_sock *tsk = tipc_sk(sk); | ||
| 824 | struct tipc_group *grp = tsk->group; | ||
| 825 | struct tipc_nlist *dsts = tipc_group_dests(grp); | ||
| 826 | struct tipc_mc_method *method = &tsk->mc_method; | ||
| 827 | struct tipc_msg *hdr = &tsk->phdr; | ||
| 828 | int mtu = tipc_bcast_get_mtu(net); | ||
| 829 | struct sk_buff_head pkts; | ||
| 830 | int rc = -EHOSTUNREACH; | ||
| 831 | |||
| 832 | if (!dsts->local && !dsts->remote) | ||
| 833 | return -EHOSTUNREACH; | ||
| 834 | |||
| 835 | /* Block or return if any destination link is congested */ | ||
| 836 | rc = tipc_wait_for_cond(sock, &timeout, !tsk->cong_link_cnt); | ||
| 837 | if (unlikely(rc)) | ||
| 838 | return rc; | ||
| 839 | |||
| 840 | /* Complete message header */ | ||
| 841 | msg_set_type(hdr, TIPC_GRP_BCAST_MSG); | ||
| 842 | msg_set_hdr_sz(hdr, MCAST_H_SIZE); | ||
| 843 | msg_set_destport(hdr, 0); | ||
| 844 | msg_set_destnode(hdr, 0); | ||
| 845 | msg_set_nameinst(hdr, 0); | ||
| 846 | msg_set_grp_bc_seqno(hdr, tipc_group_bc_snd_nxt(grp)); | ||
| 847 | |||
| 848 | /* Build message as chain of buffers */ | ||
| 849 | skb_queue_head_init(&pkts); | ||
| 850 | rc = tipc_msg_build(hdr, m, 0, dlen, mtu, &pkts); | ||
| 851 | if (unlikely(rc != dlen)) | ||
| 852 | return rc; | ||
| 853 | |||
| 854 | /* Send message */ | ||
| 855 | rc = tipc_mcast_xmit(net, &pkts, method, dsts, | ||
| 856 | &tsk->cong_link_cnt); | ||
| 857 | if (unlikely(rc)) | ||
| 858 | return rc; | ||
| 859 | |||
| 860 | /* Update broadcast sequence number */ | ||
| 861 | tipc_group_update_bc_members(tsk->group); | ||
| 862 | |||
| 863 | return dlen; | ||
| 864 | } | ||
| 865 | |||
| 866 | /** | ||
| 797 | * tipc_sk_mcast_rcv - Deliver multicast messages to all destination sockets | 867 | * tipc_sk_mcast_rcv - Deliver multicast messages to all destination sockets |
| 798 | * @arrvq: queue with arriving messages, to be cloned after destination lookup | 868 | * @arrvq: queue with arriving messages, to be cloned after destination lookup |
| 799 | * @inputq: queue with cloned messages, delivered to socket after dest lookup | 869 | * @inputq: queue with cloned messages, delivered to socket after dest lookup |
| @@ -803,13 +873,15 @@ static int tipc_sendmcast(struct socket *sock, struct tipc_name_seq *seq, | |||
| 803 | void tipc_sk_mcast_rcv(struct net *net, struct sk_buff_head *arrvq, | 873 | void tipc_sk_mcast_rcv(struct net *net, struct sk_buff_head *arrvq, |
| 804 | struct sk_buff_head *inputq) | 874 | struct sk_buff_head *inputq) |
| 805 | { | 875 | { |
| 806 | struct tipc_msg *msg; | ||
| 807 | struct list_head dports; | ||
| 808 | u32 portid; | ||
| 809 | u32 scope = TIPC_CLUSTER_SCOPE; | 876 | u32 scope = TIPC_CLUSTER_SCOPE; |
| 810 | struct sk_buff_head tmpq; | 877 | u32 self = tipc_own_addr(net); |
| 811 | uint hsz; | ||
| 812 | struct sk_buff *skb, *_skb; | 878 | struct sk_buff *skb, *_skb; |
| 879 | u32 lower = 0, upper = ~0; | ||
| 880 | struct sk_buff_head tmpq; | ||
| 881 | u32 portid, oport, onode; | ||
| 882 | struct list_head dports; | ||
| 883 | struct tipc_msg *msg; | ||
| 884 | int hsz; | ||
| 813 | 885 | ||
| 814 | __skb_queue_head_init(&tmpq); | 886 | __skb_queue_head_init(&tmpq); |
| 815 | INIT_LIST_HEAD(&dports); | 887 | INIT_LIST_HEAD(&dports); |
| @@ -818,14 +890,18 @@ void tipc_sk_mcast_rcv(struct net *net, struct sk_buff_head *arrvq, | |||
| 818 | for (; skb; skb = tipc_skb_peek(arrvq, &inputq->lock)) { | 890 | for (; skb; skb = tipc_skb_peek(arrvq, &inputq->lock)) { |
| 819 | msg = buf_msg(skb); | 891 | msg = buf_msg(skb); |
| 820 | hsz = skb_headroom(skb) + msg_hdr_sz(msg); | 892 | hsz = skb_headroom(skb) + msg_hdr_sz(msg); |
| 821 | 893 | oport = msg_origport(msg); | |
| 822 | if (in_own_node(net, msg_orignode(msg))) | 894 | onode = msg_orignode(msg); |
| 895 | if (onode == self) | ||
| 823 | scope = TIPC_NODE_SCOPE; | 896 | scope = TIPC_NODE_SCOPE; |
| 824 | 897 | ||
| 825 | /* Create destination port list and message clones: */ | 898 | /* Create destination port list and message clones: */ |
| 826 | tipc_nametbl_mc_translate(net, | 899 | if (!msg_in_group(msg)) { |
| 827 | msg_nametype(msg), msg_namelower(msg), | 900 | lower = msg_namelower(msg); |
| 828 | msg_nameupper(msg), scope, &dports); | 901 | upper = msg_nameupper(msg); |
| 902 | } | ||
| 903 | tipc_nametbl_mc_translate(net, msg_nametype(msg), lower, upper, | ||
| 904 | scope, &dports); | ||
| 829 | while (tipc_dest_pop(&dports, NULL, &portid)) { | 905 | while (tipc_dest_pop(&dports, NULL, &portid)) { |
| 830 | _skb = __pskb_copy(skb, hsz, GFP_ATOMIC); | 906 | _skb = __pskb_copy(skb, hsz, GFP_ATOMIC); |
| 831 | if (_skb) { | 907 | if (_skb) { |
| @@ -895,10 +971,6 @@ exit: | |||
| 895 | kfree_skb(skb); | 971 | kfree_skb(skb); |
| 896 | } | 972 | } |
| 897 | 973 | ||
| 898 | static void tipc_sk_top_evt(struct tipc_sock *tsk, struct tipc_event *evt) | ||
| 899 | { | ||
| 900 | } | ||
| 901 | |||
| 902 | /** | 974 | /** |
| 903 | * tipc_sendmsg - send message in connectionless manner | 975 | * tipc_sendmsg - send message in connectionless manner |
| 904 | * @sock: socket structure | 976 | * @sock: socket structure |
| @@ -934,6 +1006,7 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dlen) | |||
| 934 | long timeout = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT); | 1006 | long timeout = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT); |
| 935 | struct list_head *clinks = &tsk->cong_links; | 1007 | struct list_head *clinks = &tsk->cong_links; |
| 936 | bool syn = !tipc_sk_type_connectionless(sk); | 1008 | bool syn = !tipc_sk_type_connectionless(sk); |
| 1009 | struct tipc_group *grp = tsk->group; | ||
| 937 | struct tipc_msg *hdr = &tsk->phdr; | 1010 | struct tipc_msg *hdr = &tsk->phdr; |
| 938 | struct tipc_name_seq *seq; | 1011 | struct tipc_name_seq *seq; |
| 939 | struct sk_buff_head pkts; | 1012 | struct sk_buff_head pkts; |
| @@ -944,6 +1017,9 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dlen) | |||
| 944 | if (unlikely(dlen > TIPC_MAX_USER_MSG_SIZE)) | 1017 | if (unlikely(dlen > TIPC_MAX_USER_MSG_SIZE)) |
| 945 | return -EMSGSIZE; | 1018 | return -EMSGSIZE; |
| 946 | 1019 | ||
| 1020 | if (unlikely(grp)) | ||
| 1021 | return tipc_send_group_bcast(sock, m, dlen, timeout); | ||
| 1022 | |||
| 947 | if (unlikely(!dest)) { | 1023 | if (unlikely(!dest)) { |
| 948 | dest = &tsk->peer; | 1024 | dest = &tsk->peer; |
| 949 | if (!syn || dest->family != AF_TIPC) | 1025 | if (!syn || dest->family != AF_TIPC) |
| @@ -1543,6 +1619,7 @@ static void tipc_sk_proto_rcv(struct sock *sk, | |||
| 1543 | struct sk_buff *skb = __skb_dequeue(inputq); | 1619 | struct sk_buff *skb = __skb_dequeue(inputq); |
| 1544 | struct tipc_sock *tsk = tipc_sk(sk); | 1620 | struct tipc_sock *tsk = tipc_sk(sk); |
| 1545 | struct tipc_msg *hdr = buf_msg(skb); | 1621 | struct tipc_msg *hdr = buf_msg(skb); |
| 1622 | struct tipc_group *grp = tsk->group; | ||
| 1546 | 1623 | ||
| 1547 | switch (msg_user(hdr)) { | 1624 | switch (msg_user(hdr)) { |
| 1548 | case CONN_MANAGER: | 1625 | case CONN_MANAGER: |
| @@ -1553,8 +1630,12 @@ static void tipc_sk_proto_rcv(struct sock *sk, | |||
| 1553 | tsk->cong_link_cnt--; | 1630 | tsk->cong_link_cnt--; |
| 1554 | sk->sk_write_space(sk); | 1631 | sk->sk_write_space(sk); |
| 1555 | break; | 1632 | break; |
| 1633 | case GROUP_PROTOCOL: | ||
| 1634 | tipc_group_proto_rcv(grp, hdr, xmitq); | ||
| 1635 | break; | ||
| 1556 | case TOP_SRV: | 1636 | case TOP_SRV: |
| 1557 | tipc_sk_top_evt(tsk, (void *)msg_data(hdr)); | 1637 | tipc_group_member_evt(tsk->group, skb, xmitq); |
| 1638 | skb = NULL; | ||
| 1558 | break; | 1639 | break; |
| 1559 | default: | 1640 | default: |
| 1560 | break; | 1641 | break; |
| @@ -1699,6 +1780,7 @@ static void tipc_sk_filter_rcv(struct sock *sk, struct sk_buff *skb, | |||
| 1699 | { | 1780 | { |
| 1700 | bool sk_conn = !tipc_sk_type_connectionless(sk); | 1781 | bool sk_conn = !tipc_sk_type_connectionless(sk); |
| 1701 | struct tipc_sock *tsk = tipc_sk(sk); | 1782 | struct tipc_sock *tsk = tipc_sk(sk); |
| 1783 | struct tipc_group *grp = tsk->group; | ||
| 1702 | struct tipc_msg *hdr = buf_msg(skb); | 1784 | struct tipc_msg *hdr = buf_msg(skb); |
| 1703 | struct net *net = sock_net(sk); | 1785 | struct net *net = sock_net(sk); |
| 1704 | struct sk_buff_head inputq; | 1786 | struct sk_buff_head inputq; |
| @@ -1710,15 +1792,19 @@ static void tipc_sk_filter_rcv(struct sock *sk, struct sk_buff *skb, | |||
| 1710 | 1792 | ||
| 1711 | if (unlikely(!msg_isdata(hdr))) | 1793 | if (unlikely(!msg_isdata(hdr))) |
| 1712 | tipc_sk_proto_rcv(sk, &inputq, xmitq); | 1794 | tipc_sk_proto_rcv(sk, &inputq, xmitq); |
| 1713 | else if (unlikely(msg_type(hdr) > TIPC_DIRECT_MSG)) | 1795 | else if (unlikely(msg_type(hdr) > TIPC_GRP_BCAST_MSG)) |
| 1714 | return kfree_skb(skb); | 1796 | return kfree_skb(skb); |
| 1715 | 1797 | ||
| 1798 | if (unlikely(grp)) | ||
| 1799 | tipc_group_filter_msg(grp, &inputq, xmitq); | ||
| 1800 | |||
| 1716 | /* Validate and add to receive buffer if there is space */ | 1801 | /* Validate and add to receive buffer if there is space */ |
| 1717 | while ((skb = __skb_dequeue(&inputq))) { | 1802 | while ((skb = __skb_dequeue(&inputq))) { |
| 1718 | hdr = buf_msg(skb); | 1803 | hdr = buf_msg(skb); |
| 1719 | limit = rcvbuf_limit(sk, skb); | 1804 | limit = rcvbuf_limit(sk, skb); |
| 1720 | if ((sk_conn && !tipc_sk_filter_connect(tsk, skb)) || | 1805 | if ((sk_conn && !tipc_sk_filter_connect(tsk, skb)) || |
| 1721 | (!sk_conn && msg_connected(hdr))) | 1806 | (!sk_conn && msg_connected(hdr)) || |
| 1807 | (!grp && msg_in_group(hdr))) | ||
| 1722 | err = TIPC_ERR_NO_PORT; | 1808 | err = TIPC_ERR_NO_PORT; |
| 1723 | else if (sk_rmem_alloc_get(sk) + skb->truesize >= limit) | 1809 | else if (sk_rmem_alloc_get(sk) + skb->truesize >= limit) |
| 1724 | err = TIPC_ERR_OVERLOAD; | 1810 | err = TIPC_ERR_OVERLOAD; |
| @@ -1837,7 +1923,6 @@ void tipc_sk_rcv(struct net *net, struct sk_buff_head *inputq) | |||
| 1837 | sock_put(sk); | 1923 | sock_put(sk); |
| 1838 | continue; | 1924 | continue; |
| 1839 | } | 1925 | } |
| 1840 | |||
| 1841 | /* No destination socket => dequeue skb if still there */ | 1926 | /* No destination socket => dequeue skb if still there */ |
| 1842 | skb = tipc_skb_dequeue(inputq, dport); | 1927 | skb = tipc_skb_dequeue(inputq, dport); |
| 1843 | if (!skb) | 1928 | if (!skb) |
| @@ -1905,6 +1990,11 @@ static int tipc_connect(struct socket *sock, struct sockaddr *dest, | |||
| 1905 | 1990 | ||
| 1906 | lock_sock(sk); | 1991 | lock_sock(sk); |
| 1907 | 1992 | ||
| 1993 | if (tsk->group) { | ||
| 1994 | res = -EINVAL; | ||
| 1995 | goto exit; | ||
| 1996 | } | ||
| 1997 | |||
| 1908 | if (dst->family == AF_UNSPEC) { | 1998 | if (dst->family == AF_UNSPEC) { |
| 1909 | memset(&tsk->peer, 0, sizeof(struct sockaddr_tipc)); | 1999 | memset(&tsk->peer, 0, sizeof(struct sockaddr_tipc)); |
| 1910 | if (!tipc_sk_type_connectionless(sk)) | 2000 | if (!tipc_sk_type_connectionless(sk)) |
| @@ -2341,6 +2431,52 @@ void tipc_sk_rht_destroy(struct net *net) | |||
| 2341 | rhashtable_destroy(&tn->sk_rht); | 2431 | rhashtable_destroy(&tn->sk_rht); |
| 2342 | } | 2432 | } |
| 2343 | 2433 | ||
| 2434 | static int tipc_sk_join(struct tipc_sock *tsk, struct tipc_group_req *mreq) | ||
| 2435 | { | ||
| 2436 | struct net *net = sock_net(&tsk->sk); | ||
| 2437 | u32 domain = addr_domain(net, mreq->scope); | ||
| 2438 | struct tipc_group *grp = tsk->group; | ||
| 2439 | struct tipc_msg *hdr = &tsk->phdr; | ||
| 2440 | struct tipc_name_seq seq; | ||
| 2441 | int rc; | ||
| 2442 | |||
| 2443 | if (mreq->type < TIPC_RESERVED_TYPES) | ||
| 2444 | return -EACCES; | ||
| 2445 | if (grp) | ||
| 2446 | return -EACCES; | ||
| 2447 | grp = tipc_group_create(net, tsk->portid, mreq); | ||
| 2448 | if (!grp) | ||
| 2449 | return -ENOMEM; | ||
| 2450 | tsk->group = grp; | ||
| 2451 | msg_set_lookup_scope(hdr, mreq->scope); | ||
| 2452 | msg_set_nametype(hdr, mreq->type); | ||
| 2453 | msg_set_dest_droppable(hdr, true); | ||
| 2454 | seq.type = mreq->type; | ||
| 2455 | seq.lower = mreq->instance; | ||
| 2456 | seq.upper = seq.lower; | ||
| 2457 | tipc_nametbl_build_group(net, grp, mreq->type, domain); | ||
| 2458 | rc = tipc_sk_publish(tsk, mreq->scope, &seq); | ||
| 2459 | if (rc) | ||
| 2460 | tipc_group_delete(net, grp); | ||
| 2461 | return rc; | ||
| 2462 | } | ||
| 2463 | |||
| 2464 | static int tipc_sk_leave(struct tipc_sock *tsk) | ||
| 2465 | { | ||
| 2466 | struct net *net = sock_net(&tsk->sk); | ||
| 2467 | struct tipc_group *grp = tsk->group; | ||
| 2468 | struct tipc_name_seq seq; | ||
| 2469 | int scope; | ||
| 2470 | |||
| 2471 | if (!grp) | ||
| 2472 | return -EINVAL; | ||
| 2473 | tipc_group_self(grp, &seq, &scope); | ||
| 2474 | tipc_group_delete(net, grp); | ||
| 2475 | tsk->group = NULL; | ||
| 2476 | tipc_sk_withdraw(tsk, scope, &seq); | ||
| 2477 | return 0; | ||
| 2478 | } | ||
| 2479 | |||
| 2344 | /** | 2480 | /** |
| 2345 | * tipc_setsockopt - set socket option | 2481 | * tipc_setsockopt - set socket option |
| 2346 | * @sock: socket structure | 2482 | * @sock: socket structure |
| @@ -2359,6 +2495,7 @@ static int tipc_setsockopt(struct socket *sock, int lvl, int opt, | |||
| 2359 | { | 2495 | { |
| 2360 | struct sock *sk = sock->sk; | 2496 | struct sock *sk = sock->sk; |
| 2361 | struct tipc_sock *tsk = tipc_sk(sk); | 2497 | struct tipc_sock *tsk = tipc_sk(sk); |
| 2498 | struct tipc_group_req mreq; | ||
| 2362 | u32 value = 0; | 2499 | u32 value = 0; |
| 2363 | int res = 0; | 2500 | int res = 0; |
| 2364 | 2501 | ||
| @@ -2374,9 +2511,14 @@ static int tipc_setsockopt(struct socket *sock, int lvl, int opt, | |||
| 2374 | case TIPC_CONN_TIMEOUT: | 2511 | case TIPC_CONN_TIMEOUT: |
| 2375 | if (ol < sizeof(value)) | 2512 | if (ol < sizeof(value)) |
| 2376 | return -EINVAL; | 2513 | return -EINVAL; |
| 2377 | res = get_user(value, (u32 __user *)ov); | 2514 | if (get_user(value, (u32 __user *)ov)) |
| 2378 | if (res) | 2515 | return -EFAULT; |
| 2379 | return res; | 2516 | break; |
| 2517 | case TIPC_GROUP_JOIN: | ||
| 2518 | if (ol < sizeof(mreq)) | ||
| 2519 | return -EINVAL; | ||
| 2520 | if (copy_from_user(&mreq, ov, sizeof(mreq))) | ||
| 2521 | return -EFAULT; | ||
| 2380 | break; | 2522 | break; |
| 2381 | default: | 2523 | default: |
| 2382 | if (ov || ol) | 2524 | if (ov || ol) |
| @@ -2409,6 +2551,12 @@ static int tipc_setsockopt(struct socket *sock, int lvl, int opt, | |||
| 2409 | tsk->mc_method.rcast = true; | 2551 | tsk->mc_method.rcast = true; |
| 2410 | tsk->mc_method.mandatory = true; | 2552 | tsk->mc_method.mandatory = true; |
| 2411 | break; | 2553 | break; |
| 2554 | case TIPC_GROUP_JOIN: | ||
| 2555 | res = tipc_sk_join(tsk, &mreq); | ||
| 2556 | break; | ||
| 2557 | case TIPC_GROUP_LEAVE: | ||
| 2558 | res = tipc_sk_leave(tsk); | ||
| 2559 | break; | ||
| 2412 | default: | 2560 | default: |
| 2413 | res = -EINVAL; | 2561 | res = -EINVAL; |
| 2414 | } | 2562 | } |
| @@ -2436,7 +2584,8 @@ static int tipc_getsockopt(struct socket *sock, int lvl, int opt, | |||
| 2436 | { | 2584 | { |
| 2437 | struct sock *sk = sock->sk; | 2585 | struct sock *sk = sock->sk; |
| 2438 | struct tipc_sock *tsk = tipc_sk(sk); | 2586 | struct tipc_sock *tsk = tipc_sk(sk); |
| 2439 | int len; | 2587 | struct tipc_name_seq seq; |
| 2588 | int len, scope; | ||
| 2440 | u32 value; | 2589 | u32 value; |
| 2441 | int res; | 2590 | int res; |
| 2442 | 2591 | ||
| @@ -2470,6 +2619,12 @@ static int tipc_getsockopt(struct socket *sock, int lvl, int opt, | |||
| 2470 | case TIPC_SOCK_RECVQ_DEPTH: | 2619 | case TIPC_SOCK_RECVQ_DEPTH: |
| 2471 | value = skb_queue_len(&sk->sk_receive_queue); | 2620 | value = skb_queue_len(&sk->sk_receive_queue); |
| 2472 | break; | 2621 | break; |
| 2622 | case TIPC_GROUP_JOIN: | ||
| 2623 | seq.type = 0; | ||
| 2624 | if (tsk->group) | ||
| 2625 | tipc_group_self(tsk->group, &seq, &scope); | ||
| 2626 | value = seq.type; | ||
| 2627 | break; | ||
| 2473 | default: | 2628 | default: |
| 2474 | res = -EINVAL; | 2629 | res = -EINVAL; |
| 2475 | } | 2630 | } |
