icmp.c - vx32 - Local 9vx git repository for patches.
 (HTM) git clone git://r-36.net/vx32
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       icmp.c (9491B)
       ---
            1 #include "u.h"
            2 #include "lib.h"
            3 #include "mem.h"
            4 #include "dat.h"
            5 #include "fns.h"
            6 #include "error.h"
            7 
            8 #include "ip.h"
            9 
           10 typedef struct Icmp {
           11         uchar        vihl;                /* Version and header length */
           12         uchar        tos;                /* Type of service */
           13         uchar        length[2];        /* packet length */
           14         uchar        id[2];                /* Identification */
           15         uchar        frag[2];        /* Fragment information */
           16         uchar        ttl;                /* Time to live */
           17         uchar        proto;                /* Protocol */
           18         uchar        ipcksum[2];        /* Header checksum */
           19         uchar        src[4];                /* Ip source */
           20         uchar        dst[4];                /* Ip destination */
           21         uchar        type;
           22         uchar        code;
           23         uchar        cksum[2];
           24         uchar        icmpid[2];
           25         uchar        seq[2];
           26         uchar        data[1];
           27 } Icmp;
           28 
           29 enum {                        /* Packet Types */
           30         EchoReply        = 0,
           31         Unreachable        = 3,
           32         SrcQuench        = 4,
           33         Redirect        = 5,
           34         EchoRequest        = 8,
           35         TimeExceed        = 11,
           36         InParmProblem        = 12,
           37         Timestamp        = 13,
           38         TimestampReply        = 14,
           39         InfoRequest        = 15,
           40         InfoReply        = 16,
           41         AddrMaskRequest = 17,
           42         AddrMaskReply   = 18,
           43 
           44         Maxtype                = 18,
           45 };
           46 
           47 enum
           48 {
           49         MinAdvise        = 24,        /* minimum needed for us to advise another protocol */ 
           50 };
           51 
           52 char *icmpnames[Maxtype+1] =
           53 {
           54 [EchoReply]                "EchoReply",
           55 [Unreachable]                "Unreachable",
           56 [SrcQuench]                "SrcQuench",
           57 [Redirect]                "Redirect",
           58 [EchoRequest]                "EchoRequest",
           59 [TimeExceed]                "TimeExceed",
           60 [InParmProblem]                "InParmProblem",
           61 [Timestamp]                "Timestamp",
           62 [TimestampReply]        "TimestampReply",
           63 [InfoRequest]                "InfoRequest",
           64 [InfoReply]                "InfoReply",
           65 [AddrMaskRequest]        "AddrMaskRequest",
           66 [AddrMaskReply  ]        "AddrMaskReply  ",
           67 };
           68 
           69 enum {
           70         IP_ICMPPROTO        = 1,
           71         ICMP_IPSIZE        = 20,
           72         ICMP_HDRSIZE        = 8,
           73 };
           74 
           75 enum
           76 {
           77         InMsgs,
           78         InErrors,
           79         OutMsgs,
           80         CsumErrs,
           81         LenErrs,
           82         HlenErrs,
           83 
           84         Nstats,
           85 };
           86 
           87 static char *statnames[Nstats] =
           88 {
           89 [InMsgs]        "InMsgs",
           90 [InErrors]        "InErrors",
           91 [OutMsgs]        "OutMsgs",
           92 [CsumErrs]        "CsumErrs",
           93 [LenErrs]        "LenErrs",
           94 [HlenErrs]        "HlenErrs",
           95 };
           96 
           97 typedef struct Icmppriv Icmppriv;
           98 struct Icmppriv
           99 {
          100         ulong        stats[Nstats];
          101 
          102         /* message counts */
          103         ulong        in[Maxtype+1];
          104         ulong        out[Maxtype+1];
          105 };
          106 
          107 static void icmpkick(void *x, Block*);
          108 
          109 static void
          110 icmpcreate(Conv *c)
          111 {
          112         c->rq = qopen(64*1024, Qmsg, 0, c);
          113         c->wq = qbypass(icmpkick, c);
          114 }
          115 
          116 extern char*
          117 icmpconnect(Conv *c, char **argv, int argc)
          118 {
          119         char *e;
          120 
          121         e = Fsstdconnect(c, argv, argc);
          122         if(e != nil)
          123                 return e;
          124         Fsconnected(c, e);
          125 
          126         return nil;
          127 }
          128 
          129 extern int
          130 icmpstate(Conv *c, char *state, int n)
          131 {
          132         USED(c);
          133         return snprint(state, n, "%s qin %d qout %d\n",
          134                 "Datagram",
          135                 c->rq ? qlen(c->rq) : 0,
          136                 c->wq ? qlen(c->wq) : 0
          137         );
          138 }
          139 
          140 extern char*
          141 icmpannounce(Conv *c, char **argv, int argc)
          142 {
          143         char *e;
          144 
          145         e = Fsstdannounce(c, argv, argc);
          146         if(e != nil)
          147                 return e;
          148         Fsconnected(c, nil);
          149 
          150         return nil;
          151 }
          152 
          153 extern void
          154 icmpclose(Conv *c)
          155 {
          156         qclose(c->rq);
          157         qclose(c->wq);
          158         ipmove(c->laddr, IPnoaddr);
          159         ipmove(c->raddr, IPnoaddr);
          160         c->lport = 0;
          161 }
          162 
          163 static void
          164 icmpkick(void *x, Block *bp)
          165 {
          166         Conv *c = x;
          167         Icmp *p;
          168         Icmppriv *ipriv;
          169 
          170         if(bp == nil)
          171                 return;
          172 
          173         if(blocklen(bp) < ICMP_IPSIZE + ICMP_HDRSIZE){
          174                 freeblist(bp);
          175                 return;
          176         }
          177         p = (Icmp *)(bp->rp);
          178         p->vihl = IP_VER4;
          179         ipriv = c->p->priv;
          180         if(p->type <= Maxtype)        
          181                 ipriv->out[p->type]++;
          182         
          183         v6tov4(p->dst, c->raddr);
          184         v6tov4(p->src, c->laddr);
          185         p->proto = IP_ICMPPROTO;
          186         hnputs(p->icmpid, c->lport);
          187         memset(p->cksum, 0, sizeof(p->cksum));
          188         hnputs(p->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE));
          189         ipriv->stats[OutMsgs]++;
          190         ipoput4(c->p->f, bp, 0, c->ttl, c->tos, nil);
          191 }
          192 
          193 extern void
          194 icmpttlexceeded(Fs *f, uchar *ia, Block *bp)
          195 {
          196         Block        *nbp;
          197         Icmp        *p, *np;
          198 
          199         p = (Icmp *)bp->rp;
          200 
          201         netlog(f, Logicmp, "sending icmpttlexceeded -> %V\n", p->src);
          202         nbp = allocb(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8);
          203         nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8;
          204         np = (Icmp *)nbp->rp;
          205         np->vihl = IP_VER4;
          206         memmove(np->dst, p->src, sizeof(np->dst));
          207         v6tov4(np->src, ia);
          208         memmove(np->data, bp->rp, ICMP_IPSIZE + 8);
          209         np->type = TimeExceed;
          210         np->code = 0;
          211         np->proto = IP_ICMPPROTO;
          212         hnputs(np->icmpid, 0);
          213         hnputs(np->seq, 0);
          214         memset(np->cksum, 0, sizeof(np->cksum));
          215         hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE));
          216         ipoput4(f, nbp, 0, MAXTTL, DFLTTOS, nil);
          217 
          218 }
          219 
          220 static void
          221 icmpunreachable(Fs *f, Block *bp, int code, int seq)
          222 {
          223         Block        *nbp;
          224         Icmp        *p, *np;
          225         int        i;
          226         uchar        addr[IPaddrlen];
          227 
          228         p = (Icmp *)bp->rp;
          229 
          230         /* only do this for unicast sources and destinations */
          231         v4tov6(addr, p->dst);
          232         i = ipforme(f, addr);
          233         if((i&Runi) == 0)
          234                 return;
          235         v4tov6(addr, p->src);
          236         i = ipforme(f, addr);
          237         if(i != 0 && (i&Runi) == 0)
          238                 return;
          239 
          240         netlog(f, Logicmp, "sending icmpnoconv -> %V\n", p->src);
          241         nbp = allocb(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8);
          242         nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8;
          243         np = (Icmp *)nbp->rp;
          244         np->vihl = IP_VER4;
          245         memmove(np->dst, p->src, sizeof(np->dst));
          246         memmove(np->src, p->dst, sizeof(np->src));
          247         memmove(np->data, bp->rp, ICMP_IPSIZE + 8);
          248         np->type = Unreachable;
          249         np->code = code;
          250         np->proto = IP_ICMPPROTO;
          251         hnputs(np->icmpid, 0);
          252         hnputs(np->seq, seq);
          253         memset(np->cksum, 0, sizeof(np->cksum));
          254         hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE));
          255         ipoput4(f, nbp, 0, MAXTTL, DFLTTOS, nil);
          256 }
          257 
          258 extern void
          259 icmpnoconv(Fs *f, Block *bp)
          260 {
          261         icmpunreachable(f, bp, 3, 0);
          262 }
          263 
          264 extern void
          265 icmpcantfrag(Fs *f, Block *bp, int mtu)
          266 {
          267         icmpunreachable(f, bp, 4, mtu);
          268 }
          269 
          270 static void
          271 goticmpkt(Proto *icmp, Block *bp)
          272 {
          273         Conv        **c, *s;
          274         Icmp        *p;
          275         uchar        dst[IPaddrlen];
          276         ushort        recid;
          277 
          278         p = (Icmp *) bp->rp;
          279         v4tov6(dst, p->src);
          280         recid = nhgets(p->icmpid);
          281 
          282         for(c = icmp->conv; *c; c++) {
          283                 s = *c;
          284                 if(s->lport == recid)
          285                 if(ipcmp(s->raddr, dst) == 0){
          286                         bp = concatblock(bp);
          287                         if(bp != nil)
          288                                 qpass(s->rq, bp);
          289                         return;
          290                 }
          291         }
          292         freeblist(bp);
          293 }
          294 
          295 static Block *
          296 mkechoreply(Block *bp)
          297 {
          298         Icmp        *q;
          299         uchar        ip[4];
          300 
          301         q = (Icmp *)bp->rp;
          302         q->vihl = IP_VER4;
          303         memmove(ip, q->src, sizeof(q->dst));
          304         memmove(q->src, q->dst, sizeof(q->src));
          305         memmove(q->dst, ip,  sizeof(q->dst));
          306         q->type = EchoReply;
          307         memset(q->cksum, 0, sizeof(q->cksum));
          308         hnputs(q->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE));
          309 
          310         return bp;
          311 }
          312 
          313 static char *unreachcode[] =
          314 {
          315 [0]        "net unreachable",
          316 [1]        "host unreachable",
          317 [2]        "protocol unreachable",
          318 [3]        "port unreachable",
          319 [4]        "fragmentation needed and DF set",
          320 [5]        "source route failed",
          321 };
          322 
          323 static void
          324 icmpiput(Proto *icmp, Ipifc* __, Block *bp)
          325 {
          326         int        n, iplen;
          327         Icmp        *p;
          328         Block        *r;
          329         Proto        *pr;
          330         char        *msg;
          331         char        m2[128];
          332         Icmppriv *ipriv;
          333 
          334         ipriv = icmp->priv;
          335         
          336         ipriv->stats[InMsgs]++;
          337 
          338         p = (Icmp *)bp->rp;
          339         netlog(icmp->f, Logicmp, "icmpiput %d %d\n", p->type, p->code);
          340         n = blocklen(bp);
          341         if(n < ICMP_IPSIZE+ICMP_HDRSIZE){
          342                 ipriv->stats[InErrors]++;
          343                 ipriv->stats[HlenErrs]++;
          344                 netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
          345                 goto raise;
          346         }
          347         iplen = nhgets(p->length);
          348         if(iplen > n || ((uint)iplen % 1)){
          349                 ipriv->stats[LenErrs]++;
          350                 ipriv->stats[InErrors]++;
          351                 netlog(icmp->f, Logicmp, "icmp length %d\n", iplen);
          352                 goto raise;
          353         }
          354         if(ptclcsum(bp, ICMP_IPSIZE, iplen - ICMP_IPSIZE)){
          355                 ipriv->stats[InErrors]++;
          356                 ipriv->stats[CsumErrs]++;
          357                 netlog(icmp->f, Logicmp, "icmp checksum error\n");
          358                 goto raise;
          359         }
          360         if(p->type <= Maxtype)
          361                 ipriv->in[p->type]++;
          362 
          363         switch(p->type) {
          364         case EchoRequest:
          365                 if (iplen < n)
          366                         bp = trimblock(bp, 0, iplen);
          367                 r = mkechoreply(bp);
          368                 ipriv->out[EchoReply]++;
          369                 ipoput4(icmp->f, r, 0, MAXTTL, DFLTTOS, nil);
          370                 break;
          371         case Unreachable:
          372                 if(p->code > 5)
          373                         msg = unreachcode[1];
          374                 else
          375                         msg = unreachcode[p->code];
          376 
          377                 bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE;
          378                 if(blocklen(bp) < MinAdvise){
          379                         ipriv->stats[LenErrs]++;
          380                         goto raise;
          381                 }
          382                 p = (Icmp *)bp->rp;
          383                 pr = Fsrcvpcolx(icmp->f, p->proto);
          384                 if(pr != nil && pr->advise != nil) {
          385                         (*pr->advise)(pr, bp, msg);
          386                         return;
          387                 }
          388 
          389                 bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE;
          390                 goticmpkt(icmp, bp);
          391                 break;
          392         case TimeExceed:
          393                 if(p->code == 0){
          394                         sprint(m2, "ttl exceeded at %V", p->src);
          395 
          396                         bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE;
          397                         if(blocklen(bp) < MinAdvise){
          398                                 ipriv->stats[LenErrs]++;
          399                                 goto raise;
          400                         }
          401                         p = (Icmp *)bp->rp;
          402                         pr = Fsrcvpcolx(icmp->f, p->proto);
          403                         if(pr != nil && pr->advise != nil) {
          404                                 (*pr->advise)(pr, bp, m2);
          405                                 return;
          406                         }
          407                         bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE;
          408                 }
          409 
          410                 goticmpkt(icmp, bp);
          411                 break;
          412         default:
          413                 goticmpkt(icmp, bp);
          414                 break;
          415         }
          416         return;
          417 
          418 raise:
          419         freeblist(bp);
          420 }
          421 
          422 void
          423 icmpadvise(Proto *icmp, Block *bp, char *msg)
          424 {
          425         Conv        **c, *s;
          426         Icmp        *p;
          427         uchar        dst[IPaddrlen];
          428         ushort        recid;
          429 
          430         p = (Icmp *) bp->rp;
          431         v4tov6(dst, p->dst);
          432         recid = nhgets(p->icmpid);
          433 
          434         for(c = icmp->conv; *c; c++) {
          435                 s = *c;
          436                 if(s->lport == recid)
          437                 if(ipcmp(s->raddr, dst) == 0){
          438                         qhangup(s->rq, msg);
          439                         qhangup(s->wq, msg);
          440                         break;
          441                 }
          442         }
          443         freeblist(bp);
          444 }
          445 
          446 int
          447 icmpstats(Proto *icmp, char *buf, int len)
          448 {
          449         Icmppriv *priv;
          450         char *p, *e;
          451         int i;
          452 
          453         priv = icmp->priv;
          454         p = buf;
          455         e = p+len;
          456         for(i = 0; i < Nstats; i++)
          457                 p = seprint(p, e, "%s: %lud\n", statnames[i], priv->stats[i]);
          458         for(i = 0; i <= Maxtype; i++){
          459                 if(icmpnames[i])
          460                         p = seprint(p, e, "%s: %lud %lud\n", icmpnames[i], priv->in[i], priv->out[i]);
          461                 else
          462                         p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i], priv->out[i]);
          463         }
          464         return p - buf;
          465 }
          466         
          467 void
          468 icmpinit(Fs *fs)
          469 {
          470         Proto *icmp;
          471 
          472         icmp = smalloc(sizeof(Proto));
          473         icmp->priv = smalloc(sizeof(Icmppriv));
          474         icmp->name = "icmp";
          475         icmp->connect = icmpconnect;
          476         icmp->announce = icmpannounce;
          477         icmp->state = icmpstate;
          478         icmp->create = icmpcreate;
          479         icmp->close = icmpclose;
          480         icmp->rcv = icmpiput;
          481         icmp->stats = icmpstats;
          482         icmp->ctl = nil;
          483         icmp->advise = icmpadvise;
          484         icmp->gc = nil;
          485         icmp->ipproto = IP_ICMPPROTO;
          486         icmp->nc = 128;
          487         icmp->ptclsize = 0;
          488 
          489         Fsproto(fs, icmp);
          490 }