ref: 98a66671cf6900b6239ac1b48638ffa835e75c27
parent: 0ed8a3bd7b1bbe2a3857f2abcaabdc4edd2d8b05
author: cinap_lenrek <[email protected]>
date: Sun Nov 20 10:38:36 EST 2022
devip: lilu dallas multicast. Allow accepting udp "connections" using a multicast local address. Before, it was only possible to receive multicast using the headers option. Having a connection orirented stream can be very usefull when receiving multicast audio data. One gets a "connection" for every source. Implement (optional) support for IGMPv2 and MLDv1. This can be usefull if bridges on the network have IGMP/MLD snooping enabled, and wont forward multicast traffic unless we report what we excpect. This is experimental for now, so the igmp protocol must be manually added to the kernel configuration.
--- a/sys/src/9/ip/devip.c
+++ b/sys/src/9/ip/devip.c
@@ -1173,27 +1173,27 @@
if(cb->nf < 2)
error("addmulti needs interface address");
if(cb->nf == 2){
- if(!ipismulticast(c->raddr))
- error("addmulti for a non multicast address");
if (parseip(ia, cb->f[1]) == -1)
error(Ebadip);
- ipifcaddmulti(c, c->raddr, ia);
+ if(ipcmp(c->raddr, IPnoaddr) == 0 || ipismulticast(c->laddr))
+ ipifcaddmulti(c, c->laddr, ia);
+ else
+ ipifcaddmulti(c, c->raddr, ia);
} else {
if (parseip(ia, cb->f[1]) == -1 ||
parseip(ma, cb->f[2]) == -1)
error(Ebadip);
- if(!ipismulticast(ma))
- error("addmulti for a non multicast address");
ipifcaddmulti(c, ma, ia);
}
} else if(strcmp(cb->f[0], "remmulti") == 0){
if(cb->nf < 2)
error("remmulti needs interface address");
- if(!ipismulticast(c->raddr))
- error("remmulti for a non multicast address");
if (parseip(ia, cb->f[1]) == -1)
error(Ebadip);
- ipifcremmulti(c, c->raddr, ia);
+ if(ipcmp(c->raddr, IPnoaddr) == 0 || ipismulticast(c->laddr))
+ ipifcremmulti(c, c->laddr, ia);
+ else
+ ipifcremmulti(c, c->raddr, ia);
} else if(x->ctl != nil) {
p = (*x->ctl)(c, cb->f, cb->nf);
if(p != nil)
@@ -1258,7 +1258,7 @@
p->f = f;
- if(p->ipproto > 0){
+ if(p->ipproto >= 0){
if(f->t2p[p->ipproto] != nil)
return -1;
f->t2p[p->ipproto] = p;
--- a/sys/src/9/ip/igmp.c
+++ b/sys/src/9/ip/igmp.c
@@ -1,6 +1,5 @@
/*
- * igmp - internet group management protocol
- * unfinished.
+ * IGMPv2 - internet group management protocol (and MLDv1)
*/
#include "u.h"
#include "../port/lib.h"
@@ -10,6 +9,7 @@
#include "../port/error.h"
#include "ip.h"
+#include "ipv6.h"
enum
{
@@ -19,7 +19,15 @@
IGMPquery = 1,
IGMPreport = 2,
+ IGMPv2report = 6,
+ IGMPv2leave = 7,
+ IP_MLDPROTO = HBH, /* hop-by-hop header */
+
+ MLDquery = 130,
+ MLDreport = 131,
+ MLDdone = 132,
+
MSPTICK = 100,
MAXTIMEOUT = 10000/MSPTICK, /* at most 10 secs for a response */
};
@@ -36,14 +44,14 @@
uchar Unused;
uchar proto; /* Protocol */
uchar cksum[2]; /* checksum of ip portion */
- uchar src[IPaddrlen]; /* Ip source */
- uchar dst[IPaddrlen]; /* Ip destination */
+ uchar src[4]; /* Ip source */
+ uchar dst[4]; /* Ip destination */
/* igmp header */
uchar vertype; /* version and type */
- uchar unused;
- uchar igmpcksum[2]; /* checksum of igmp portion */
- uchar group[IPaddrlen]; /* multicast group */
+ uchar resptime;
+ uchar igmpcksum[2]; /* checksum of igmp portion */
+ uchar group[4]; /* multicast group */
uchar payload[];
};
@@ -50,116 +58,177 @@
#define IGMPPKTSZ offsetof(IGMPpkt, payload[0])
-/*
- * lists for group reports
- */
-typedef struct IGMPrep IGMPrep;
-struct IGMPrep
-{
- IGMPrep *next;
- Medium *m;
- int ticks;
- Multicast *multi;
-};
+typedef struct MLDpkt MLDpkt;
+struct MLDpkt {
+ IPV6HDR;
-typedef struct IGMP IGMP;
-struct IGMP
-{
- Lock;
- Rendez r;
- IGMPrep *reports;
+ uchar type;
+ uchar code;
+ uchar cksum[2];
+ uchar delay[2];
+ uchar res[2];
+ uchar group[IPaddrlen];
+ uchar payload[];
};
-IGMP igmpalloc;
+#define MLDPKTSZ offsetof(MLDpkt, payload[0])
- Proto igmp;
-extern Fs fs;
+static uchar mldhbhopt[] = {
+ ICMPv6, /* NextHeader */
+ 0x00, /* HeaderLength */
+ 0x05, /* Option: Router Alert */
+ 0x02, /* Length */
+ 0x00, 0x00, /* MLD */
-static struct Stats
+ 0x01, /* Option: PadN */
+ 0x00, /* Length */
+};
+
+typedef struct Report Report;
+struct Report
{
- ulong inqueries;
- ulong outqueries;
- ulong inreports;
- ulong outreports;
-} stats;
+ Report *next;
+ Proto *proto;
+ Ipifc *ifc;
+ int ifcid;
+ int timeout; /* in MSPTICK's */
+ Ipmulti *multi;
+};
-void
-igmpsendreport(Medium *m, uchar *addr)
+typedef struct Priv Priv;
+struct Priv
{
+ QLock;
+ Rendez r;
+ Report *reports;
+};
+
+static void
+igmpsendreport(Fs *f, uchar *src, uchar *dst, uchar *group, int done)
+{
IGMPpkt *p;
Block *bp;
- bp = allocb(sizeof(IGMPpkt));
- p = (IGMPpkt*)bp->wp;
- p->vihl = IP_VER4;
+ bp = allocb(IGMPPKTSZ);
bp->wp += IGMPPKTSZ;
- memset(bp->rp, 0, IGMPPKTSZ);
- hnputl(p->src, Mediumgetaddr(m));
- hnputl(p->dst, Ipallsys);
- p->vertype = (1<<4) | IGMPreport;
+ p = (IGMPpkt*)bp->rp;
+ memset(p, 0, IGMPPKTSZ);
+ p->vihl = IP_VER4;
+ memmove(p->src, src+IPv4off, IPv4addrlen);
+ memmove(p->dst, dst+IPv4off, IPv4addrlen);
+ p->vertype = (1<<4) | (done? IGMPv2leave: IGMPv2report);
+ p->resptime = 0;
p->proto = IP_IGMPPROTO;
- memmove(p->group, addr, IPaddrlen);
+ memmove(p->group, group+IPv4off, IPv4addrlen);
hnputs(p->igmpcksum, ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE));
- netlog(Logigmp, "igmpreport %I\n", p->group);
- stats.outreports++;
- ipoput4(bp, 0, 1, DFLTTOS, nil); /* TTL of 1 */
+ ipoput4(f, bp, 0, 1, DFLTTOS, nil); /* TTL of 1 */
}
+static void
+mldsendreport(Fs *f, uchar *src, uchar *dst, uchar *group, int done)
+{
+ MLDpkt *p;
+ Block *bp;
+
+ if(!islinklocal(src))
+ return;
+
+ bp = allocb(sizeof(mldhbhopt)+MLDPKTSZ);
+ bp->wp += sizeof(mldhbhopt)+MLDPKTSZ;
+ bp->rp += sizeof(mldhbhopt);
+ p = (MLDpkt*)bp->rp;
+ memset(p, 0, MLDPKTSZ);
+ ipmove(p->src, src);
+ ipmove(p->dst, dst);
+ p->type = done? MLDdone: MLDreport;
+ p->code = 0;
+ ipmove(p->group, group);
+
+ /* generate checksum */
+ hnputl(p->vcf, 0);
+ hnputs(p->ploadlen, MLDPKTSZ-IP6HDR);
+ p->proto = 0;
+ p->ttl = ICMPv6; /* ttl gets set later */
+ hnputs(p->cksum, 0);
+ hnputs(p->cksum, ptclcsum(bp, 0, MLDPKTSZ));
+
+ /* add hop-by-hop option header */
+ bp->rp -= sizeof(mldhbhopt);
+ memmove(bp->rp, p, IP6HDR);
+ memmove(bp->rp + IP6HDR, mldhbhopt, sizeof(mldhbhopt));
+ p = (MLDpkt*)bp->rp;
+ p->proto = IP_MLDPROTO;
+ hnputs(p->ploadlen, BLEN(bp) - IP6HDR);
+
+ ipoput6(f, bp, 0, 1, DFLTTOS, nil); /* TTL of 1 */
+}
+
+static void
+sendreport(Proto *pr, uchar *ia, uchar *group, int done)
+{
+ switch(pr->ipproto){
+ case IP_IGMPPROTO:
+ igmpsendreport(pr->f, ia, group, group, done);
+ break;
+ case IP_MLDPROTO:
+ mldsendreport(pr->f, ia, group, group, done);
+ break;
+ }
+}
+
static int
isreport(void *a)
{
- USED(a);
- return igmpalloc.reports != 0;
+ return ((Priv*)a)->reports != 0;
}
-
-void
+static void
igmpproc(void *a)
{
- IGMPrep *rp, **lrp;
- Multicast *mp, **lmp;
- uchar ip[IPaddrlen];
+ Proto *pr, *igmp = a;
+ Priv *priv = igmp->priv;
+ Report *rp, **lrp;
+ Ipmulti *mp, **lmp;
- USED(a);
-
for(;;){
- sleep(&igmpalloc.r, isreport, 0);
+ sleep(&priv->r, isreport, priv);
for(;;){
- lock(&igmpalloc);
-
- if(igmpalloc.reports == nil)
+ qlock(priv);
+ if(priv->reports == nil)
break;
/* look for a single report */
- lrp = &igmpalloc.reports;
mp = nil;
- for(rp = *lrp; rp; rp = *lrp){
- rp->ticks++;
+ pr = nil;
+ lrp = &priv->reports;
+ for(rp = *lrp; rp != nil; rp = *lrp){
lmp = &rp->multi;
- for(mp = *lmp; mp; mp = *lmp){
- if(rp->ticks >= mp->timeout){
+ for(mp = *lmp; mp != nil; mp = *lmp){
+ if(rp->timeout <= 1 || nrand(rp->timeout) == 0){
*lmp = mp->next;
break;
}
lmp = &mp->next;
}
- if(mp != nil)
- break;
-
+ pr = rp->proto;
if(rp->multi != nil){
+ rp->timeout--;
lrp = &rp->next;
- continue;
} else {
*lrp = rp->next;
free(rp);
}
+ if(mp != nil)
+ break;
}
- unlock(&igmpalloc);
+ qunlock(priv);
- if(mp){
+ if(mp != nil){
/* do a single report and try again */
- hnputl(ip, mp->addr);
- igmpsendreport(rp->m, ip);
+ if(pr != nil && !waserror()){
+ sendreport(pr, mp->ia, mp->ma, 0);
+ poperror();
+ }
free(mp);
continue;
}
@@ -166,92 +235,69 @@
tsleep(&up->sleep, return0, 0, MSPTICK);
}
- unlock(&igmpalloc);
+ qunlock(priv);
}
-
}
-void
-igmpiput(Medium *m, Ipifc *, Block *bp)
+/*
+ * find report list for this protocol and interface
+ */
+static Report*
+findreport(Report *rp, Proto *pr, Ipifc *ifc)
{
- int n;
- IGMPpkt *ghp;
- Ipaddr group;
- IGMPrep *rp, **lrp;
- Multicast *mp, **lmp;
+ for(; rp != nil; rp = rp->next)
+ if(rp->proto == pr && rp->ifc == ifc && rp->ifcid == ifc->ifcid)
+ return rp;
- ghp = (IGMPpkt*)(bp->rp);
- netlog(Logigmp, "igmpiput: %d %I\n", ghp->vertype, ghp->group);
+ return nil;
+}
- n = blocklen(bp);
- if(n < IGMP_IPHDRSIZE+IGMP_HDRSIZE){
- netlog(Logigmp, "igmpiput: bad len\n");
- goto error;
- }
- if((ghp->vertype>>4) != 1){
- netlog(Logigmp, "igmpiput: bad igmp type\n");
- goto error;
- }
- if(ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE)){
- netlog(Logigmp, "igmpiput: checksum error %I\n", ghp->src);
- goto error;
- }
+static void
+queuereport(Proto *pr, Ipifc *ifc, uchar *group, int timeout)
+{
+ Priv *priv = pr->priv;
+ Report *rp;
- group = nhgetl(ghp->group);
-
- lock(&igmpalloc);
- switch(ghp->vertype & 0xf){
- case IGMPquery:
+ qlock(priv);
+ if(findreport(priv->reports, pr, ifc) != nil){
/*
- * start reporting groups that we're a member of.
+ * we are already reporting on this interface,
+ * wait for the report to time-out.
*/
- stats.inqueries++;
- for(rp = igmpalloc.reports; rp; rp = rp->next)
- if(rp->m == m)
- break;
- if(rp != nil)
- break; /* already reporting */
+ qunlock(priv);
+ return;
+ }
- mp = Mediumcopymulti(m);
- if(mp == nil)
- break;
+ /*
+ * start reporting groups that we're a member of.
+ */
+ rp = smalloc(sizeof(Report));
+ rp->proto = pr;
+ rp->ifc = ifc;
+ rp->ifcid = ifc->ifcid;
+ rp->timeout = (timeout < 1 || timeout > MAXTIMEOUT) ? MAXTIMEOUT : timeout;
+ rp->multi = ipifcgetmulti(pr->f, ifc, group);
- rp = malloc(sizeof(*rp));
- if(rp == nil)
- break;
+ rp->next = priv->reports;
+ priv->reports = rp;
- rp->m = m;
- rp->multi = mp;
- rp->ticks = 0;
- for(; mp; mp = mp->next)
- mp->timeout = nrand(MAXTIMEOUT);
- rp->next = igmpalloc.reports;
- igmpalloc.reports = rp;
+ wakeup(&priv->r);
+ qunlock(priv);
+}
- wakeup(&igmpalloc.r);
+static void
+purgereport(Proto *pr, Ipifc *ifc, uchar *group)
+{
+ Priv *priv = pr->priv;
+ Report *rp;
- break;
- case IGMPreport:
- /*
- * find report list for this medium
- */
- stats.inreports++;
- lrp = &igmpalloc.reports;
- for(rp = *lrp; rp; rp = *lrp){
- if(rp->m == m)
- break;
- lrp = &rp->next;
- }
- if(rp == nil)
- break;
+ qlock(priv);
+ if((rp = findreport(priv->reports, pr, ifc)) != nil){
+ Ipmulti *mp, **lmp;
- /*
- * if someone else has reported a group,
- * we don't have to.
- */
lmp = &rp->multi;
for(mp = *lmp; mp; mp = *lmp){
- if(mp->addr == group){
+ if(ipcmp(mp->ma, group) == 0){
*lmp = mp->next;
free(mp);
break;
@@ -258,40 +304,139 @@
}
lmp = &mp->next;
}
+ }
+ qunlock(priv);
+}
+static void
+mldiput(Proto *mld, Ipifc *ifc, Block *bp)
+{
+ MLDpkt *p;
+ uchar *opt, *payload;
+
+ p = (MLDpkt*)(bp->rp);
+ /* check we have the hop-by-hop header */
+ if((p->vcf[0] & 0xF0) != IP_VER6 || p->proto != IP_MLDPROTO)
+ goto error;
+ if(p->ttl != 1 || !isv6mcast(p->dst) || !islinklocal(p->src))
+ goto error;
+
+ /* strip the hop-by-hop option header */
+ if(BLEN(bp) < IP6HDR+sizeof(mldhbhopt))
+ goto error;
+ opt = bp->rp + IP6HDR;
+ if(opt[0] != ICMPv6)
+ goto error;
+ payload = opt + ((int)opt[1] + 1)*8;
+ if(payload >= bp->wp)
+ goto error;
+ if(memcmp(opt+2, mldhbhopt+2, 4) != 0)
+ goto error;
+ memmove(payload-IP6HDR, bp->rp, IP6HDR);
+ bp->rp = payload - IP6HDR;
+ if(BLEN(bp) < MLDPKTSZ)
+ goto error;
+ p = (MLDpkt*)bp->rp;
+
+ /* verify ICMPv6 checksum */
+ hnputl(p->vcf, 0); /* borrow IP header as pseudoheader */
+ p->ttl = ICMPv6;
+ p->proto = 0;
+ hnputs(p->ploadlen, MLDPKTSZ-IP6HDR);
+ if(ptclcsum(bp, 0, MLDPKTSZ))
+ goto error;
+
+ if(ipcmp(p->group, IPnoaddr) != 0 && ipismulticast(p->group) != V6)
+ goto error;
+
+ switch(p->type){
+ case MLDquery:
+ queuereport(mld, ifc, p->group, nhgets(p->delay)/MSPTICK);
break;
+ case MLDreport:
+ purgereport(mld, ifc, p->group);
+ break;
}
- unlock(&igmpalloc);
+error:
+ freeblist(bp);
+}
+static void
+igmpiput(Proto *igmp, Ipifc *ifc, Block *bp)
+{
+ uchar group[IPaddrlen];
+ IGMPpkt *p;
+
+ p = (IGMPpkt*)bp->rp;
+ if((p->vihl & 0xF0) != IP_VER4)
+ goto error;
+ if(BLEN(bp) < IGMP_IPHDRSIZE+IGMP_HDRSIZE)
+ goto error;
+ if((p->vertype>>4) != 1)
+ goto error;
+ if(ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE))
+ goto error;
+
+ v4tov6(group, p->group);
+ if(ipcmp(group, v4prefix) != 0 && ipismulticast(group) != V4)
+ goto error;
+
+ switch(p->vertype & 0xF){
+ case IGMPquery:
+ queuereport(igmp, ifc, group, p->resptime);
+ break;
+ case IGMPreport:
+ case IGMPv2report:
+ purgereport(igmp, ifc, group);
+ break;
+ }
error:
- freeb(bp);
+ freeblist(bp);
}
-int
-igmpstats(char *buf, int len)
+static void
+multicastreport(Fs *f, Ipifc *ifc, uchar *ma, uchar *ia, int done)
{
- return snprint(buf, len, "\trcvd %d %d\n\tsent %d %d\n",
- stats.inqueries, stats.inreports,
- stats.outqueries, stats.outreports);
+ Proto *pr = Fsrcvpcolx(f, isv4(ma)? IP_IGMPPROTO: IP_MLDPROTO);
+ purgereport(pr, ifc, ma);
+ sendreport(pr, ia, ma, done);
}
void
-igmpinit(Fs *fs)
+igmpinit(Fs *f)
{
- igmp.name = "igmp";
- igmp.connect = nil;
- igmp.announce = nil;
- igmp.ctl = nil;
- igmp.state = nil;
- igmp.close = nil;
- igmp.rcv = igmpiput;
- igmp.stats = igmpstats;
- igmp.ipproto = IP_IGMPPROTO;
- igmp.nc = 0;
- igmp.ptclsize = 0;
+ Proto *igmp, *mld;
- igmpreportfn = igmpsendreport;
- kproc("igmpproc", igmpproc, 0);
+ igmp = smalloc(sizeof(Proto));
+ igmp->priv = smalloc(sizeof(Priv));
+ igmp->name = "igmp";
+ igmp->connect = nil;
+ igmp->announce = nil;
+ igmp->ctl = nil;
+ igmp->state = nil;
+ igmp->close = nil;
+ igmp->rcv = igmpiput;
+ igmp->stats = nil;
+ igmp->ipproto = IP_IGMPPROTO;
+ igmp->nc = 0;
+ igmp->ptclsize = 0;
+ Fsproto(f, igmp);
- Fsproto(fs, &igmp);
+ mld = smalloc(sizeof(Proto));
+ mld->priv = igmp->priv;
+ mld->name = "mld";
+ mld->connect = nil;
+ mld->announce = nil;
+ mld->ctl = nil;
+ mld->state = nil;
+ mld->close = nil;
+ mld->rcv = mldiput;
+ mld->stats = nil;
+ mld->ipproto = IP_MLDPROTO;
+ mld->nc = 0;
+ mld->ptclsize = 0;
+ Fsproto(f, mld);
+
+ multicastreportfn = multicastreport;
+ kproc("igmpproc", igmpproc, igmp);
}
--- a/sys/src/9/ip/ip.h
+++ b/sys/src/9/ip/ip.h
@@ -725,6 +725,7 @@
extern Iplifc* iplocalonifc(Ipifc *ifc, uchar *ip);
extern Iplifc* ipremoteonifc(Ipifc *ifc, uchar *ip);
extern int ipproxyifc(Fs *f, Ipifc *ifc, uchar *ip);
+extern Ipmulti* ipifcgetmulti(Fs *f, Ipifc *ifc, uchar *ma);
extern void ipifcremmulti(Conv *c, uchar *ma, uchar *ia);
extern void ipifcaddmulti(Conv *c, uchar *ma, uchar *ia);
extern char* ipifcrem(Ipifc *ifc, char **argv, int argc);
@@ -777,4 +778,4 @@
/*
* global to all of the stack
*/
-extern void (*igmpreportfn)(Ipifc*, uchar*);
+extern void (*multicastreportfn)(Fs*, Ipifc*, uchar*, uchar*, int);
--- a/sys/src/9/ip/ipaux.c
+++ b/sys/src/9/ip/ipaux.c
@@ -196,8 +196,7 @@
void
ipv62smcast(uchar *smcast, uchar *a)
{
- assert(IPaddrlen == 16);
- memmove(smcast, v6solicitednode, IPaddrlen);
+ ipmove(smcast, v6solicitednode);
smcast[13] = a[13];
smcast[14] = a[14];
smcast[15] = a[15];
--- a/sys/src/9/ip/ipifc.c
+++ b/sys/src/9/ip/ipifc.c
@@ -19,6 +19,7 @@
};
Medium *media[Maxmedia] = { 0 };
+void (*multicastreportfn)(Fs*, Ipifc*, uchar*, uchar*, int);
/*
* cache of local addresses (addresses we answer to)
@@ -40,18 +41,6 @@
Ipself *hash[NHASH]; /* hash chains */
};
-/*
- * Multicast addresses are chained onto a Chan so that
- * we can remove them when the Chan is closed.
- */
-typedef struct Ipmcast Ipmcast;
-struct Ipmcast
-{
- Ipmcast *next;
- uchar ma[IPaddrlen]; /* multicast address */
- uchar ia[IPaddrlen]; /* interface address */
-};
-
/* quick hash for ip addresses */
#define hashipa(a) (((a)[IPaddrlen-2] + (a)[IPaddrlen-1])%NHASH)
@@ -611,6 +600,9 @@
addselfcache(f, ifc, lifc, bcast, Rbcast);
addselfcache(f, ifc, lifc, IPv4bcast, Rbcast);
+
+ /* add all nodes multicast address */
+ addselfcache(f, ifc, lifc, IPv4allsys, Rmulti);
} else {
if(ipcmp(ip, v6loopback) == 0) {
/* add node-local mcast address */
@@ -684,11 +676,11 @@
/* remove route for all nodes multicast */
if((lifc->type & Rv4) == 0){
- if(ipcmp(lifc->local, v6loopback) == 0)
+ if(ipcmp(lifc->local, v6loopback) == 0){
remroute(f, v6allnodesN, v6allnodesNmask,
lifc->local, IPallbits,
v6allnodesN, Rmulti, ifc, tifc);
-
+ }
remroute(f, v6allnodesL, v6allnodesLmask,
lifc->local, IPallbits,
v6allnodesL, Rmulti, ifc, tifc);
@@ -960,8 +952,12 @@
IPallbits : IPnoaddr,
a, type, ifc, tifc);
- if((type & Rmulti) && ifc->m->addmulti != nil)
- (*ifc->m->addmulti)(ifc, a, lifc->local);
+ if(type & Rmulti){
+ if(ifc->m->addmulti != nil)
+ (*ifc->m->addmulti)(ifc, a, lifc->local);
+ if(multicastreportfn != nil)
+ (*multicastreportfn)(f, ifc, a, lifc->local, 0);
+ }
} else
lp->ref++;
@@ -1072,6 +1068,26 @@
if(--(link->ref) != 0)
goto out;
+ /* ref == 0, remove from both chains and free the link */
+ *l_lifc = link->lifclink;
+ *l_self = link->selflink;
+ iplinkfree(link);
+
+ if((p->type & Rmulti) != 0){
+ if(multicastreportfn != nil){
+ if(!waserror()){
+ (*multicastreportfn)(f, ifc, a, lifc->local, 1);
+ poperror();
+ }
+ }
+ if(ifc->m->remmulti != nil){
+ if(!waserror()){
+ (*ifc->m->remmulti)(ifc, a, lifc->local);
+ poperror();
+ }
+ }
+ }
+
/* remove from routing table */
remroute(f, a, IPallbits,
lifc->local,
@@ -1079,18 +1095,6 @@
IPallbits : IPnoaddr,
a, p->type, ifc, tifc);
- if((p->type & Rmulti) && ifc->m->remmulti != nil){
- if(!waserror()){
- (*ifc->m->remmulti)(ifc, a, lifc->local);
- poperror();
- }
- }
-
- /* ref == 0, remove from both chains and free the link */
- *l_lifc = link->lifclink;
- *l_self = link->selflink;
- iplinkfree(link);
-
if(p->link != nil)
goto out;
@@ -1423,7 +1427,21 @@
return nil;
}
+/*
+ * return link-local interface
+ */
+Iplifc*
+iplinklocalifc(Ipifc *ifc)
+{
+ Iplifc *lifc;
+ for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next)
+ if(islinklocal(lifc->local))
+ return lifc;
+
+ return nil;
+}
+
/*
* See if we're proxying for this address on this interface
*/
@@ -1441,6 +1459,65 @@
}
/*
+ * Return a copy of the multicast addresses on the
+ * specified interface and multicast address.
+ */
+Ipmulti*
+ipifcgetmulti(Fs *f, Ipifc *ifc, uchar *ma)
+{
+ Ipmulti *head, *tail, *mcast;
+ Iplifc *lifc;
+ Iplink *link;
+ Ipself *self;
+ int type;
+
+ type = Rmulti;
+ if(isv4(ma))
+ type |= Rv4;
+ if(ipismulticast(ma) == ((type & Rv4)? V4: V6)){
+ if(ipforme(f, ma) != Rmulti)
+ return nil;
+ } else {
+ ma = nil;
+ }
+
+ head = tail = nil;
+ for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next) {
+ if((type & Rv4) == 0 && !islinklocal(lifc->local))
+ continue;
+
+ for(link = lifc->link; link != nil; link = link->lifclink) {
+ self = link->self;
+ if((self->type & (Rv4|Rmulti)) != type)
+ continue;
+
+ if(ma != nil){
+ if(ipcmp(self->a, ma) != 0)
+ continue;
+ } else {
+ if(ipcmp(self->a, (type & Rv4)? IPv4allsys: v6allnodesL) == 0)
+ continue;
+ }
+
+ mcast = smalloc(sizeof(Ipmulti));
+ mcast->next = nil;
+ ipmove(mcast->ma, self->a);
+ ipmove(mcast->ia, lifc->local);
+
+ if(ma != nil)
+ return mcast;
+
+ if(head == nil)
+ head = mcast;
+ else
+ tail->next = mcast;
+ tail = mcast;
+ }
+ }
+ return head;
+}
+
+/*
* add a multicast address to an interface.
*/
void
@@ -1451,6 +1528,8 @@
Ipifc *ifc;
Fs *f;
+ if(!ipismulticast(ma))
+ error("addmulti for a non multicast address");
if(isv4(ma) != isv4(ia))
error("incompatible multicast/interface ip address");
@@ -1467,6 +1546,11 @@
}
if((lifc = iplocalonifc(ifc, ia)) != nil)
addselfcache(f, ifc, lifc, ma, Rmulti);
+
+ /* for IPv6, must add also add to the link-local address */
+ if(!isv4(ia) && !islinklocal(ia) && (lifc = iplinklocalifc(ifc)) != nil)
+ addselfcache(f, ifc, lifc, ma, Rmulti);
+
runlock(ifc);
poperror();
}
@@ -1506,6 +1590,11 @@
rlock(ifc);
if(!waserror()){
if((lifc = iplocalonifc(ifc, ia)) != nil)
+ remselfcache(f, ifc, lifc, ma);
+ poperror();
+ }
+ if(!isv4(ia) && !islinklocal(ia) && !waserror()){
+ if((lifc = iplinklocalifc(ifc)) != nil)
remselfcache(f, ifc, lifc, ma);
poperror();
}
--- a/sys/src/9/ip/ipv6.h
+++ b/sys/src/9/ip/ipv6.h
@@ -78,7 +78,6 @@
/* various flags & constants */
v6MINTU = 1280,
- HOP_LIMIT = 255,
IP6HDR = 40, /* sizeof(Ip6hdr) = 8 + 2*16 */
IP6FHDR = 8, /* sizeof(Fraghdr6) */
--- a/sys/src/9/ip/netlog.c
+++ b/sys/src/9/ip/netlog.c
@@ -42,6 +42,7 @@
{ "il", Logil, },
{ "tcp", Logtcp, },
{ "icmp", Logicmp, },
+ { "igmp", Logigmp, },
{ "udp", Logudp, },
{ "compress", Logcompress, },
{ "logilmsg", Logilmsg, },
--- a/sys/src/9/ip/udp.c
+++ b/sys/src/9/ip/udp.c
@@ -100,6 +100,9 @@
struct Udpcb
{
uchar headers;
+
+ /* source ip used for transmission */
+ uchar srcip[IPaddrlen];
};
static char*
@@ -113,6 +116,7 @@
Fsconnected(c, e);
if(e != nil)
return e;
+ ipmove(((Udpcb*)c->ptcl)->srcip, c->laddr);
iphtadd(&upriv->ht, c);
return nil;
}
@@ -139,6 +143,7 @@
if(e != nil)
return e;
Fsconnected(c, nil);
+ ipmove(((Udpcb*)c->ptcl)->srcip, c->laddr);
iphtadd(&upriv->ht, c);
return nil;
}
@@ -170,6 +175,7 @@
ucb = (Udpcb*)c->ptcl;
ucb->headers = 0;
+ ipmove(ucb->srcip, IPnoaddr);
}
void
@@ -214,6 +220,7 @@
bp->rp += 2+2; /* Ignore local port */
break;
default:
+ ipmove(laddr, IPnoaddr);
rport = 0;
break;
}
@@ -225,6 +232,7 @@
version = V6;
} else {
version = convipvers(c);
+ ipmove(laddr, ucb->srcip);
}
dlen = blocklen(bp);
@@ -243,16 +251,13 @@
if(ucb->headers) {
v6tov4(uh4->udpdst, raddr);
hnputs(uh4->udpdport, rport);
- v6tov4(uh4->udpsrc, laddr);
rh = nil;
} else {
v6tov4(uh4->udpdst, c->raddr);
hnputs(uh4->udpdport, c->rport);
- if(ipcmp(c->laddr, IPnoaddr) == 0)
- findlocalip(f, c->laddr, c->raddr);
- v6tov4(uh4->udpsrc, c->laddr);
rh = c;
}
+ v6tov4(uh4->udpsrc, laddr);
hnputs(uh4->udpsport, c->lport);
hnputs(uh4->udplen, ptcllen);
uh4->udpcksum[0] = 0;
@@ -279,16 +284,13 @@
if(ucb->headers) {
ipmove(uh6->udpdst, raddr);
hnputs(uh6->udpdport, rport);
- ipmove(uh6->udpsrc, laddr);
rh = nil;
} else {
ipmove(uh6->udpdst, c->raddr);
hnputs(uh6->udpdport, c->rport);
- if(ipcmp(c->laddr, IPnoaddr) == 0)
- findlocalip(f, c->laddr, c->raddr);
- ipmove(uh6->udpsrc, c->laddr);
rh = c;
}
+ ipmove(uh6->udpsrc, laddr);
hnputs(uh6->udpsport, c->lport);
hnputs(uh6->udplen, ptcllen);
uh6->udpcksum[0] = 0;
@@ -394,6 +396,7 @@
qlock(udp);
iph = iphtlook(&upriv->ht, raddr, rport, laddr, lport);
if(iph == nil){
+Noconv:
/* no conversation found */
upriv->ustats.udpNoPorts++;
qunlock(udp);
@@ -438,9 +441,23 @@
if(c->state == Announced){
if(ucb->headers == 0){
- /* create a new conversation */
- if(ipforme(f, laddr) != Runi)
- ipv6local(ifc, laddr, 0, raddr);
+ uchar srcip[IPaddrlen];
+
+ switch(ipforme(f, laddr)){
+ default:
+ goto Noconv;
+ case Runi:
+ ipmove(srcip, laddr);
+ break;
+ case Rmulti:
+ case Rbcast:
+ /*
+ * use the multicast address for reception,
+ * and local unicast ip for transmission.
+ */
+ ipv6local(ifc, srcip, 0, raddr);
+ break;
+ }
c = Fsnewcall(c, raddr, rport, laddr, lport, version);
if(c == nil){
qunlock(udp);
@@ -447,8 +464,9 @@
freeblist(bp);
return;
}
- iphtadd(&upriv->ht, c);
ucb = (Udpcb*)c->ptcl;
+ ipmove(ucb->srcip, srcip);
+ iphtadd(&upriv->ht, c);
}
}