| #include <u.h> |
| #include <libc.h> |
| #include <ip.h> |
| #include <libsec.h> |
| #include "dat.h" |
| #include "protos.h" |
| |
| /* PPP stuff */ |
| enum { |
| PPP_addr= 0xff, |
| PPP_ctl= 0x3, |
| PPP_period= 3*1000, /* period of retransmit process (in ms) */ |
| }; |
| |
| /* PPP protocols */ |
| enum { |
| PPP_ip= 0x21, /* internet */ |
| PPP_vjctcp= 0x2d, /* compressing van jacobson tcp */ |
| PPP_vjutcp= 0x2f, /* uncompressing van jacobson tcp */ |
| PPP_ml= 0x3d, /* multi link */ |
| PPP_comp= 0xfd, /* compressed packets */ |
| PPP_ipcp= 0x8021, /* ip control */ |
| PPP_ccp= 0x80fd, /* compression control */ |
| PPP_passwd= 0xc023, /* passwd authentication */ |
| PPP_lcp= 0xc021, /* link control */ |
| PPP_lqm= 0xc025, /* link quality monitoring */ |
| PPP_chap= 0xc223, /* challenge/response */ |
| }; |
| |
| /* LCP protocol (and IPCP) */ |
| |
| |
| typedef struct Lcppkt Lcppkt; |
| struct Lcppkt |
| { |
| uchar code; |
| uchar id; |
| uchar len[2]; |
| uchar data[1]; |
| }; |
| |
| typedef struct Lcpopt Lcpopt; |
| struct Lcpopt |
| { |
| uchar type; |
| uchar len; |
| uchar data[1]; |
| }; |
| |
| enum |
| { |
| /* LCP codes */ |
| Lconfreq= 1, |
| Lconfack= 2, |
| Lconfnak= 3, |
| Lconfrej= 4, |
| Ltermreq= 5, |
| Ltermack= 6, |
| Lcoderej= 7, |
| Lprotorej= 8, |
| Lechoreq= 9, |
| Lechoack= 10, |
| Ldiscard= 11, |
| Lresetreq= 14, /* for ccp only */ |
| Lresetack= 15, /* for ccp only */ |
| |
| /* Lcp configure options */ |
| Omtu= 1, |
| Octlmap= 2, |
| Oauth= 3, |
| Oquality= 4, |
| Omagic= 5, |
| Opc= 7, |
| Oac= 8, |
| |
| /* authentication protocols */ |
| APmd5= 5, |
| APmschap= 128, |
| |
| /* Chap codes */ |
| Cchallenge= 1, |
| Cresponse= 2, |
| Csuccess= 3, |
| Cfailure= 4, |
| |
| /* ipcp configure options */ |
| Oipaddrs= 1, |
| Oipcompress= 2, |
| Oipaddr= 3, |
| Oipdns= 129, |
| Oipwins= 130, |
| Oipdns2= 131, |
| Oipwins2= 132 |
| }; |
| |
| char * |
| lcpcode[] = { |
| 0, |
| "confreq", |
| "confack", |
| "confnak", |
| "confrej", |
| "termreq", |
| "termack", |
| "coderej", |
| "protorej", |
| "echoreq", |
| "echoack", |
| "discard", |
| "id", |
| "timeremain", |
| "resetreq", |
| "resetack" |
| }; |
| |
| static Mux p_mux[] = |
| { |
| {"ip", PPP_ip, }, |
| {"ppp_vjctcp", PPP_vjctcp, }, |
| {"ppp_vjutcp", PPP_vjutcp, }, |
| {"ppp_ml", PPP_ml, }, |
| {"ppp_comp", PPP_comp, }, |
| {"ppp_ipcp", PPP_ipcp, }, |
| {"ppp_ccp", PPP_ccp, }, |
| {"ppp_passwd", PPP_passwd, }, |
| {"ppp_lcp", PPP_lcp, }, |
| {"ppp_lqm", PPP_lqm, }, |
| {"ppp_chap", PPP_chap, }, |
| {0} |
| }; |
| |
| enum |
| { |
| OOproto |
| }; |
| |
| static void |
| p_compile(Filter *f) |
| { |
| Mux *m; |
| |
| for(m = p_mux; m->name != nil; m++) |
| if(strcmp(f->s, m->name) == 0){ |
| f->pr = m->pr; |
| f->ulv = m->val; |
| f->subop = OOproto; |
| return; |
| } |
| |
| sysfatal("unknown ppp field or protocol: %s", f->s); |
| } |
| |
| static int |
| p_filter(Filter *f, Msg *m) |
| { |
| int proto; |
| int len; |
| |
| if(f->subop != OOproto) |
| return 0; |
| |
| len = m->pe - m->ps; |
| if(len < 3) |
| return -1; |
| |
| if(m->ps[0] == PPP_addr && m->ps[1] == PPP_ctl) |
| m->ps += 2; |
| |
| proto = *m->ps++; |
| if((proto&1) == 0) |
| proto = (proto<<8) | *m->ps++; |
| |
| if(proto == f->ulv) |
| return 1; |
| |
| return 0; |
| } |
| |
| static int |
| p_seprint(Msg *m) |
| { |
| int proto; |
| int len; |
| |
| len = m->pe - m->ps; |
| if(len < 3) |
| return -1; |
| |
| if(m->ps[0] == PPP_addr && m->ps[1] == PPP_ctl) |
| m->ps += 2; |
| |
| proto = *m->ps++; |
| if((proto&1) == 0) |
| proto = (proto<<8) | *m->ps++; |
| |
| m->p = seprint(m->p, m->e, "pr=%ud len=%d", proto, len); |
| demux(p_mux, proto, proto, m, &dump); |
| |
| return 0; |
| } |
| |
| static int |
| p_seprintchap(Msg *m) |
| { |
| Lcppkt *lcp; |
| char *p, *e; |
| int len; |
| |
| if(m->pe-m->ps < 4) |
| return -1; |
| |
| p = m->p; |
| e = m->e; |
| m->pr = nil; |
| |
| /* resize packet */ |
| lcp = (Lcppkt*)m->ps; |
| len = NetS(lcp->len); |
| if(m->ps+len < m->pe) |
| m->pe = m->ps+len; |
| else if(m->ps+len > m->pe) |
| return -1; |
| |
| p = seprint(p, e, "id=%d code=%d", lcp->id, lcp->code); |
| switch(lcp->code) { |
| default: |
| p = seprint(p, e, " data=%.*H", len>64?64:len, lcp->data); |
| break; |
| case 1: |
| case 2: |
| if(lcp->data[0] > len-4){ |
| p = seprint(p, e, "%.*H", len-4, lcp->data); |
| } else { |
| p = seprint(p, e, " %s=", lcp->code==1?"challenge ":"response "); |
| p = seprint(p, e, "%.*H", lcp->data[0], lcp->data+1); |
| p = seprint(p, e, " name="); |
| p = seprint(p, e, "%.*H", len-4-lcp->data[0]-1, lcp->data+lcp->data[0]+1); |
| } |
| break; |
| case 3: |
| case 4: |
| if(len > 64) |
| len = 64; |
| p = seprint(p, e, " %s=%.*H", lcp->code==3?"success ":"failure", |
| len>64?64:len, lcp->data); |
| break; |
| } |
| m->p = seprint(p, e, " len=%d", len); |
| return 0; |
| } |
| |
| static char* |
| seprintlcpopt(char *p, char *e, void *a, int len) |
| { |
| Lcpopt *o; |
| int proto, x, period; |
| uchar *cp, *ecp; |
| |
| cp = a; |
| ecp = cp+len; |
| |
| for(; cp < ecp; cp += o->len){ |
| o = (Lcpopt*)cp; |
| if(cp + o->len > ecp || o->len == 0){ |
| p = seprint(p, e, " bad-opt-len=%d", o->len); |
| return p; |
| } |
| |
| switch(o->type){ |
| default: |
| p = seprint(p, e, " (type=%d len=%d)", o->type, o->len); |
| break; |
| case Omtu: |
| p = seprint(p, e, " mtu=%d", NetS(o->data)); |
| break; |
| case Octlmap: |
| p = seprint(p, e, " ctlmap=%ux", NetL(o->data)); |
| break; |
| case Oauth: |
| proto = NetS(o->data); |
| switch(proto) { |
| default: |
| p = seprint(p, e, " auth=%d", proto); |
| break; |
| case PPP_passwd: |
| p = seprint(p, e, " auth=passwd"); |
| break; |
| case PPP_chap: |
| p = seprint(p, e, " (auth=chap data=%2.2ux)", o->data[2]); |
| break; |
| } |
| break; |
| case Oquality: |
| proto = NetS(o->data); |
| switch(proto) { |
| default: |
| p = seprint(p, e, " qproto=%d", proto); |
| break; |
| case PPP_lqm: |
| x = NetL(o->data+2)*10; |
| period = (x+(PPP_period-1))/PPP_period; |
| p = seprint(p, e, " (qproto=lqm period=%d)", period); |
| break; |
| } |
| case Omagic: |
| p = seprint(p, e, " magic=%ux", NetL(o->data)); |
| break; |
| case Opc: |
| p = seprint(p, e, " protocol-compress"); |
| break; |
| case Oac: |
| p = seprint(p, e, " addr-compress"); |
| break; |
| } |
| } |
| return p; |
| } |
| |
| |
| static int |
| p_seprintlcp(Msg *m) |
| { |
| Lcppkt *lcp; |
| char *p, *e; |
| int len; |
| |
| if(m->pe-m->ps < 4) |
| return -1; |
| |
| p = m->p; |
| e = m->e; |
| m->pr = nil; |
| |
| lcp = (Lcppkt*)m->ps; |
| len = NetS(lcp->len); |
| if(m->ps+len < m->pe) |
| m->pe = m->ps+len; |
| else if(m->ps+len > m->pe) |
| return -1; |
| |
| p = seprint(p, e, "id=%d code=%d", lcp->id, lcp->code); |
| switch(lcp->code) { |
| default: |
| p = seprint(p, e, " data=%.*H", len>64?64:len, lcp->data); |
| break; |
| case Lconfreq: |
| case Lconfack: |
| case Lconfnak: |
| case Lconfrej: |
| p = seprint(p, e, "=%s", lcpcode[lcp->code]); |
| p = seprintlcpopt(p, e, lcp->data, len-4); |
| break; |
| case Ltermreq: |
| case Ltermack: |
| case Lcoderej: |
| case Lprotorej: |
| case Lechoreq: |
| case Lechoack: |
| case Ldiscard: |
| p = seprint(p, e, "=%s", lcpcode[lcp->code]); |
| break; |
| } |
| m->p = seprint(p, e, " len=%d", len); |
| return 0; |
| } |
| |
| static char* |
| seprintipcpopt(char *p, char *e, void *a, int len) |
| { |
| Lcpopt *o; |
| uchar *cp, *ecp; |
| |
| cp = a; |
| ecp = cp+len; |
| |
| for(; cp < ecp; cp += o->len){ |
| o = (Lcpopt*)cp; |
| if(cp + o->len > ecp){ |
| p = seprint(p, e, " bad opt len %ux", o->type); |
| return p; |
| } |
| |
| switch(o->type){ |
| default: |
| p = seprint(p, e, " (type=%d len=%d)", o->type, o->len); |
| break; |
| case Oipaddrs: |
| p = seprint(p, e, " ipaddrs(deprecated)"); |
| break; |
| case Oipcompress: |
| p = seprint(p, e, " ipcompress"); |
| break; |
| case Oipaddr: |
| p = seprint(p, e, " ipaddr=%V", o->data); |
| break; |
| case Oipdns: |
| p = seprint(p, e, " dnsaddr=%V", o->data); |
| break; |
| case Oipwins: |
| p = seprint(p, e, " winsaddr=%V", o->data); |
| break; |
| case Oipdns2: |
| p = seprint(p, e, " dns2addr=%V", o->data); |
| break; |
| case Oipwins2: |
| p = seprint(p, e, " wins2addr=%V", o->data); |
| break; |
| } |
| } |
| return p; |
| } |
| |
| static int |
| p_seprintipcp(Msg *m) |
| { |
| Lcppkt *lcp; |
| char *p, *e; |
| int len; |
| |
| if(m->pe-m->ps < 4) |
| return -1; |
| |
| p = m->p; |
| e = m->e; |
| m->pr = nil; |
| |
| lcp = (Lcppkt*)m->ps; |
| len = NetS(lcp->len); |
| if(m->ps+len < m->pe) |
| m->pe = m->ps+len; |
| else if(m->ps+len > m->pe) |
| return -1; |
| |
| p = seprint(p, e, "id=%d code=%d", lcp->id, lcp->code); |
| switch(lcp->code) { |
| default: |
| p = seprint(p, e, " data=%.*H", len>64?64:len, lcp->data); |
| break; |
| case Lconfreq: |
| case Lconfack: |
| case Lconfnak: |
| case Lconfrej: |
| p = seprint(p, e, "=%s", lcpcode[lcp->code]); |
| p = seprintipcpopt(p, e, lcp->data, len-4); |
| break; |
| case Ltermreq: |
| case Ltermack: |
| p = seprint(p, e, "=%s", lcpcode[lcp->code]); |
| break; |
| } |
| m->p = seprint(p, e, " len=%d", len); |
| return 0; |
| } |
| |
| static char* |
| seprintccpopt(char *p, char *e, void *a, int len) |
| { |
| Lcpopt *o; |
| uchar *cp, *ecp; |
| |
| cp = a; |
| ecp = cp+len; |
| |
| for(; cp < ecp; cp += o->len){ |
| o = (Lcpopt*)cp; |
| if(cp + o->len > ecp){ |
| p = seprint(p, e, " bad opt len %ux", o->type); |
| return p; |
| } |
| |
| switch(o->type){ |
| default: |
| p = seprint(p, e, " type=%d ", o->type); |
| break; |
| case 0: |
| p = seprint(p, e, " OUI=(%d %.2ux%.2ux%.2ux) ", o->type, |
| o->data[0], o->data[1], o->data[2]); |
| break; |
| case 17: |
| p = seprint(p, e, " Stac-LZS"); |
| break; |
| case 18: |
| p = seprint(p, e, " Microsoft-PPC=%ux", NetL(o->data)); |
| break; |
| } |
| } |
| return p; |
| } |
| |
| static int |
| p_seprintccp(Msg *m) |
| { |
| Lcppkt *lcp; |
| char *p, *e; |
| int len; |
| |
| if(m->pe-m->ps < 4) |
| return -1; |
| |
| p = m->p; |
| e = m->e; |
| m->pr = nil; |
| |
| lcp = (Lcppkt*)m->ps; |
| len = NetS(lcp->len); |
| if(m->ps+len < m->pe) |
| m->pe = m->ps+len; |
| else if(m->ps+len > m->pe) |
| return -1; |
| |
| p = seprint(p, e, "id=%d code=%d", lcp->id, lcp->code); |
| switch(lcp->code) { |
| default: |
| p = seprint(p, e, " data=%.*H", len>64?64:len, lcp->data); |
| break; |
| case Lconfreq: |
| case Lconfack: |
| case Lconfnak: |
| case Lconfrej: |
| p = seprint(p, e, "=%s", lcpcode[lcp->code]); |
| p = seprintccpopt(p, e, lcp->data, len-4); |
| break; |
| case Ltermreq: |
| case Ltermack: |
| case Lresetreq: |
| case Lresetack: |
| p = seprint(p, e, "=%s", lcpcode[lcp->code]); |
| break; |
| } |
| m->p = seprint(p, e, " len=%d", len); |
| |
| return 0; |
| } |
| |
| static int |
| p_seprintcomp(Msg *m) |
| { |
| char compflag[5]; |
| ushort x; |
| int i; |
| int len; |
| |
| len = m->pe-m->ps; |
| if(len < 2) |
| return -1; |
| |
| x = NetS(m->ps); |
| m->ps += 2; |
| i = 0; |
| if(x & (1<<15)) |
| compflag[i++] = 'r'; |
| if(x & (1<<14)) |
| compflag[i++] = 'f'; |
| if(x & (1<<13)) |
| compflag[i++] = 'c'; |
| if(x & (1<<12)) |
| compflag[i++] = 'e'; |
| compflag[i] = 0; |
| m->p = seprint(m->p, m->e, "flag=%s count=%.3ux", compflag, x&0xfff); |
| m->p = seprint(m->p, m->e, " data=%.*H", len>64?64:len, m->ps); |
| m->pr = nil; |
| return 0; |
| } |
| |
| Proto ppp = |
| { |
| "ppp", |
| p_compile, |
| p_filter, |
| p_seprint, |
| p_mux, |
| "%#.4lux", |
| nil, |
| defaultframer |
| }; |
| |
| Proto ppp_ipcp = |
| { |
| "ppp_ipcp", |
| p_compile, |
| p_filter, |
| p_seprintipcp, |
| nil, |
| nil, |
| nil, |
| defaultframer |
| }; |
| |
| Proto ppp_lcp = |
| { |
| "ppp_lcp", |
| p_compile, |
| p_filter, |
| p_seprintlcp, |
| nil, |
| nil, |
| nil, |
| defaultframer |
| }; |
| |
| Proto ppp_ccp = |
| { |
| "ppp_ccp", |
| p_compile, |
| p_filter, |
| p_seprintccp, |
| nil, |
| nil, |
| nil, |
| defaultframer |
| }; |
| |
| Proto ppp_chap = |
| { |
| "ppp_chap", |
| p_compile, |
| p_filter, |
| p_seprintchap, |
| nil, |
| nil, |
| nil, |
| defaultframer |
| }; |
| |
| Proto ppp_comp = |
| { |
| "ppp_comp", |
| p_compile, |
| p_filter, |
| p_seprintcomp, |
| nil, |
| nil, |
| nil, |
| defaultframer |
| }; |