Commit 69acf64f authored by Pavel Emelyanov's avatar Pavel Emelyanov

packet: Add support for mclists

The implementation is rather straightforward. One thing to note
is that non-single membership of each type is not supported. It
can be done, but I'm unaware of any software doing so.

Note: the pb show routine should be tuned to support showing bytes.
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent 712979a4
......@@ -52,6 +52,7 @@ struct packet_diag_info {
struct packet_diag_mclist {
__u32 pdmc_index;
__u32 pdmc_count;
__u16 pdmc_type;
__u16 pdmc_alen;
__u8 pdmc_addr[MAX_ADDR_LEN];
......
import "fown.proto";
import "sk-opts.proto";
message packet_mclist {
required uint32 index = 1;
required uint32 type = 2;
required bytes addr = 3;
}
message packet_sock_entry {
required uint32 id = 1;
required uint32 type = 2;
......@@ -19,4 +25,5 @@ message packet_sock_entry {
required bool loss = 13;
required uint32 timestamp = 14;
required uint32 copy_thresh = 15;
repeated packet_mclist mclist = 16;
}
......@@ -21,11 +21,20 @@ struct packet_sock_info {
struct file_desc d;
};
struct packet_mreq_max {
int mr_ifindex;
unsigned short mr_type;
unsigned short mr_alen;
unsigned char mr_address[MAX_ADDR_LEN];
};
struct packet_sock_desc {
struct socket_desc sd;
unsigned int type;
unsigned short proto;
struct packet_diag_info nli;
int mreq_n;
struct packet_diag_mclist *mreqs;
};
void show_packetsk(int fd, struct cr_options *o)
......@@ -33,11 +42,71 @@ void show_packetsk(int fd, struct cr_options *o)
pb_show_plain(fd, PB_PACKETSK);
}
static int dump_mreqs(PacketSockEntry *psk, struct packet_sock_desc *sd)
{
int i;
if (!sd->mreq_n)
return 0;
pr_debug("\tdumping %d mreqs\n", sd->mreq_n);
psk->mclist = xmalloc(sd->mreq_n * sizeof(psk->mclist[0]));
if (!psk->mclist)
return -1;
for (i = 0; i < sd->mreq_n; i++) {
struct packet_diag_mclist *m = &sd->mreqs[i];
PacketMclist *im;
if (m->pdmc_count != 1) {
pr_err("Multiple MC membership not supported (but can be)\n");
goto err;
}
pr_debug("\tmr%d: idx %d type %d\n", i,
m->pdmc_index, m->pdmc_type);
im = xmalloc(sizeof(*im));
if (!im)
goto err;
packet_mclist__init(im);
psk->mclist[i] = im;
psk->n_mclist++;
im->index = m->pdmc_index;
im->type = m->pdmc_type;
switch (m->pdmc_type) {
case PACKET_MR_MULTICAST:
case PACKET_MR_UNICAST:
im->addr.len = m->pdmc_alen;
im->addr.data = xmalloc(m->pdmc_alen);
if (!im->addr.data)
goto err;
memcpy(im->addr.data, m->pdmc_addr, m->pdmc_alen);
break;
case PACKET_MR_PROMISC:
case PACKET_MR_ALLMULTI:
break;
default:
pr_err("Unknown mc membership type %d\n", m->pdmc_type);
goto err;
}
}
return 0;
err:
return -1;
}
static int dump_one_packet_fd(int lfd, u32 id, const struct fd_parms *p)
{
PacketSockEntry psk = PACKET_SOCK_ENTRY__INIT;
SkOptsEntry skopts = SK_OPTS_ENTRY__INIT;
struct packet_sock_desc *sd;
int i, ret;
sd = (struct packet_sock_desc *)lookup_socket(p->stat.st_ino, PF_PACKET);
if (sd < 0)
......@@ -67,7 +136,16 @@ static int dump_one_packet_fd(int lfd, u32 id, const struct fd_parms *p)
psk.vnet_hdr = (sd->nli.pdi_flags & PDI_VNETHDR ? true : false);
psk.loss = (sd->nli.pdi_flags & PDI_LOSS ? true : false);
return pb_write_one(fdset_fd(glob_fdset, CR_FD_PACKETSK), &psk, PB_PACKETSK);
ret = dump_mreqs(&psk, sd);
if (ret)
goto out;
ret = pb_write_one(fdset_fd(glob_fdset, CR_FD_PACKETSK), &psk, PB_PACKETSK);
out:
for (i = 0; i < psk.n_mclist; i++)
xfree(psk.mclist[i]->addr.data);
xfree(psk.mclist);
return ret;
}
static const struct fdtype_ops packet_dump_ops = {
......@@ -80,6 +158,18 @@ int dump_one_packet_sk(struct fd_parms *p, int lfd, const struct cr_fdset *fds)
return do_dump_gen_file(p, lfd, &packet_dump_ops, fds);
}
static int packet_save_mreqs(struct packet_sock_desc *sd, struct rtattr *mc)
{
sd->mreq_n = RTA_PAYLOAD(mc) / sizeof(struct packet_diag_mclist);
pr_debug("\tGot %d mreqs\n", sd->mreq_n);
sd->mreqs = xmalloc(RTA_PAYLOAD(mc));
if (!sd->mreqs)
return -1;
memcpy(sd->mreqs, RTA_DATA(mc), RTA_PAYLOAD(mc));
return 0;
}
int packet_receive_one(struct nlmsghdr *hdr, void *arg)
{
struct packet_diag_msg *m;
......@@ -96,6 +186,11 @@ int packet_receive_one(struct nlmsghdr *hdr, void *arg)
return -1;
}
if (!tb[PACKET_DIAG_MCLIST]) {
pr_err("No packet sock mclist in nlm\n");
return -1;
}
sd = xmalloc(sizeof(*sd));
if (!sd)
return -1;
......@@ -104,9 +199,40 @@ int packet_receive_one(struct nlmsghdr *hdr, void *arg)
sd->proto = htons(m->pdiag_num);
memcpy(&sd->nli, RTA_DATA(tb[PACKET_DIAG_INFO]), sizeof(sd->nli));
if (packet_save_mreqs(sd, tb[PACKET_DIAG_MCLIST]))
return -1;
return sk_collect_one(m->pdiag_ino, PF_PACKET, &sd->sd);
}
static int restore_mreqs(int sk, PacketSockEntry *pse)
{
int i;
for (i = 0; i < pse->n_mclist; i++) {
PacketMclist *ml;
struct packet_mreq_max mreq;
ml = pse->mclist[i];
pr_info("Restoring mreq type %d\n", ml->type);
if (ml->addr.len > sizeof(mreq.mr_address)) {
pr_err("To big mcaddr %lu\n", ml->addr.len);
return -1;
}
mreq.mr_ifindex = ml->index;
mreq.mr_type = ml->type;
mreq.mr_alen = ml->addr.len;
memcpy(mreq.mr_address, ml->addr.data, ml->addr.len);
if (restore_opt(sk, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq))
return -1;
}
return 0;
}
static int open_packet_sk(struct file_desc *d)
{
struct packet_sock_info *psi;
......@@ -170,6 +296,9 @@ static int open_packet_sk(struct file_desc *d)
goto err_cl;
}
if (restore_mreqs(sk, pse))
goto err_cl;
if (rst_file_params(sk, pse->fown, pse->flags))
goto err_cl;
......
......@@ -273,7 +273,7 @@ int collect_sockets(int pid)
req.r.p.sdiag_family = AF_PACKET;
req.r.p.sdiag_protocol = 0;
req.r.p.pdiag_show = PACKET_SHOW_INFO;
req.r.p.pdiag_show = PACKET_SHOW_INFO | PACKET_SHOW_MCLIST;
tmp = do_rtnl_req(nl, &req, sizeof(req), packet_receive_one, NULL);
if (tmp)
err = tmp;
......
......@@ -49,12 +49,26 @@ static int test_sockaddr(int n, struct sockaddr_ll *have, struct sockaddr_ll *wa
return 0;
}
#ifndef MAX_ADDR_LEN
#define MAX_ADDR_LEN 32
#endif
struct packet_mreq_max {
int mr_ifindex;
unsigned short mr_type;
unsigned short mr_alen;
unsigned char mr_address[MAX_ADDR_LEN];
};
#define LO_ADDR_LEN 6
int main(int argc, char **argv)
{
int sk1, sk2;
struct sockaddr_ll addr, addr1, addr2;
socklen_t alen;
int ver, rsv, yes;
struct packet_mreq_max mreq;
test_init(argc, argv);
......@@ -114,6 +128,23 @@ int main(int argc, char **argv)
return 1;
}
memset(&mreq, 0, sizeof(mreq));
mreq.mr_ifindex = 1;
mreq.mr_type = PACKET_MR_PROMISC;
if (setsockopt(sk1, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
err("Can't add promisc member %m");
return 1;
}
memset(&mreq, 0, sizeof(mreq));
mreq.mr_ifindex = 1;
mreq.mr_type = PACKET_MR_UNICAST;
mreq.mr_alen = LO_ADDR_LEN;
if (setsockopt(sk2, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
err("Can't add ucast member %m");
return 1;
}
test_daemon();
test_waitsig();
......@@ -148,6 +179,14 @@ int main(int argc, char **argv)
return 1;
}
memset(&mreq, 0, sizeof(mreq));
mreq.mr_ifindex = 1;
mreq.mr_type = PACKET_MR_PROMISC;
if (setsockopt(sk1, SOL_PACKET, PACKET_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
fail("Promisc member not kept");
return 1;
}
alen = sizeof(addr);
if (getsockname(sk2, (struct sockaddr *)&addr, &alen) < 0) {
fail("Can't get sockname 2 rst");
......@@ -179,6 +218,15 @@ int main(int argc, char **argv)
return 1;
}
memset(&mreq, 0, sizeof(mreq));
mreq.mr_ifindex = 1;
mreq.mr_type = PACKET_MR_UNICAST;
mreq.mr_alen = LO_ADDR_LEN;
if (setsockopt(sk2, SOL_PACKET, PACKET_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
fail("Ucast member not kept");
return 1;
}
pass();
return 0;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment