/* * IP_MASQ_SIP - SIP masquerading module * * * Author: Billy Biggs * * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Copyright (c) 2000 3Com Corporation * * Fixes: * 31 Mar 2000 - Fix to the 'ipaddr' parameter code, wasn't properly switching * maddr * 23 Mar 2000 - Added module parameter 'ipaddr' to explicitly use an address * instead of the masquerading hosts' own ip address */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ISALPHA(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z')) #define ISDIGIT(c) (((c) >= '0') && ((c) <= '9')) /* * List of ports (up to MAX_MASQ_APP_PORTS) to be handled by the mangler * First port is set to the default port. */ int ports[MAX_MASQ_APP_PORTS] = {5060, 0}; /* I rely on the trailing items being set to zero */ struct ip_masq_app *masq_incarnations[MAX_MASQ_APP_PORTS]; /* * In some situations, the masquerading host is not aware of its own globally * routable address (the masquerader itself is behind a NAT device). In order * to accommodate this, we have added a module parameter to allow for an * explicit masquerading address to be set. This is the address which will be * placed in the body of the SIP messages. */ static char *ipaddr = 0; static __u32 emaddr; struct ip_masq_timeout_table sip_timeout_table = { ATOMIC_INIT(0), /* refcnt */ 0, /* scale */ { 30*60*HZ, /* IP_MASQ_S_NONE, */ 15*60*HZ, /* IP_MASQ_S_ESTABLISHED, */ 1, /* IP_MASQ_S_SYN_SENT, */ 1, /* IP_MASQ_S_SYN_RECV, */ 1, /* IP_MASQ_S_FIN_WAIT, */ 1, /* IP_MASQ_S_TIME_WAIT, */ 1, /* IP_MASQ_S_CLOSE, */ 1, /* IP_MASQ_S_CLOSE_WAIT, */ 1, /* IP_MASQ_S_LAST_ACK, */ 1, /* IP_MASQ_S_LISTEN, */ 60*60*HZ, /* IP_MASQ_S_UDP, */ 1, /* IP_MASQ_S_ICMP, */ 2*HZ, /* IP_MASQ_S_LAST */ }, /* timeout */ }; /* * Debug level */ #ifdef CONFIG_IP_MASQ_DEBUG static int debug=0; MODULE_PARM(debug, "i"); #endif MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i"); MODULE_PARM(ipaddr, "s"); static int masq_sip_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms) { MOD_INC_USE_COUNT; return 0; } static int masq_sip_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms) { MOD_DEC_USE_COUNT; return 0; } /** * searches for the body of a sip message and returns the position in the message */ int masq_sip_find_body_start (char *data, int len) { char *orig_data = data; char *data_limit = data + len - 4; while (data < data_limit) { if (!memcmp(data,"\n\n",2) || !memcmp(data,"\n\r",2)) { data += 2; return (data - orig_data); } if (!memcmp(data,"\r\n\r\n",4)) { data += 4; return (data - orig_data); } data++; } return 0; } /** * mangles the topmost via header to contain the masq host and port */ int masq_sip_mangle_via (struct sk_buff **skb_p, char *buf, unsigned buf_len) { struct sk_buff *skb; struct iphdr *iph; struct udphdr *uh; char *data; char *data_limit; char *addrstart; int diff = 0; /* this is our current buffer - do we need this? */ skb = *skb_p; iph = skb->nh.iph; uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]); /* lets keep the original around please */ data = (char *)&uh[1]; /* We can search the packet up until here, being overly safe for now */ data_limit = skb->h.raw + skb->len - 18; /* mod up the via */ while (data < data_limit) { /* find the topmost tag */ if (strnicmp(data,"\nvia:",5) && strnicmp(data,"\nv:",3) && strnicmp(data,"\rvia:",5) && strnicmp(data,"\rv:",3)) { data++; continue; } IP_MASQ_DEBUG(1-debug, "masq_sip: Found via header\n"); /* Look for UDP, since this masq module only does udp anyways */ while (*data!='U' && *data!='u') { if (data==data_limit) return 0; data++; } data += 3; if (data>=data_limit) return 0; /* skip white space */ while (*data==' ') { if (data==data_limit) return 0; data++; } /* grab the ip address */ addrstart = data; /* this accepts FQDNs or dotted quads */ while (ISALPHA(*data) || ISDIGIT(*data) || (*data=='.') || (*data=='-')) { data++; if (data==data_limit) return 0; } /* skip the port information too, if any */ if (*data==':') { data++; if (data==data_limit) return 0; while (ISDIGIT(*data)) { data++; if (data==data_limit) return 0; } } /* replace the skb */ *skb_p = ip_masq_skb_replace(skb, GFP_ATOMIC, addrstart, data-addrstart, buf, buf_len); /* we're supposed to return this */ diff += (buf_len - (data-addrstart)); IP_MASQ_DEBUG(1-debug, "masq_sip: Via header replaced\n"); break; } return diff; } /** * mangle the contact header to have the ipaddress be the masq addr */ int masq_sip_mangle_contact (struct sk_buff **skb_p, char *buf, unsigned buf_len) { struct sk_buff *skb; struct iphdr *iph; struct udphdr *uh; char *data; char *data_limit; char *starturi; char *addrstart; int diff = 0; /* this is our current buffer - do we need this? */ skb = *skb_p; iph = skb->nh.iph; uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]); data = (char *)&uh[1]; /* We can search the packet up until here, being overly safe for now */ data_limit = skb->h.raw + skb->len - 4; /* mod up the contact */ while (data < data_limit) { /* find the topmost tag */ if (strnicmp(data,"\ncontact:",9) && strnicmp(data,"\nm:",3) && strnicmp(data,"\rcontact:",9) && strnicmp(data,"\rm:",3)) { data++; continue; } IP_MASQ_DEBUG(1-debug, "masq_sip: Found contact header\n"); /* Look for sip: */ while (strnicmp(data,"sip:",4)) { if (data == data_limit) break; data++; } data += 4; /* we have to look to see if there's user info in the contact header */ starturi = data; while (*data!='@' && *data!='>' && *data!=';' && *data!='\n' && *data!='\r' && *data!='?' && *data!=',') { if (data == data_limit) break; data++; } /* check for userinfo */ if (*data=='@') { /* woop! */ data++; } else { data = starturi; } /* we should be fine now */ addrstart = data; /* this accepts FQDNs or dotted quads */ while (ISALPHA(*data) || ISDIGIT(*data) || (*data=='.') || (*data=='-')) { data++; if (data == data_limit) break; } /* skip the port information too, if any */ if (*data==':') { data++; while (ISDIGIT(*data)) data++; } *skb_p = ip_masq_skb_replace(skb, GFP_ATOMIC, addrstart, data-addrstart, buf, buf_len); /* we're supposed to return this */ diff += (buf_len - (data-addrstart)); IP_MASQ_DEBUG(1-debug, "masq_sip: Contact tag replaced\n"); break; } return diff; } /** * update the content length header */ int masq_sip_mangle_content_length (struct sk_buff **skb_p, int newlength) { struct sk_buff *skb; struct iphdr *iph; struct udphdr *uh; char *data; char *data_limit; char lenbuf[32]; unsigned lenbuf_len; char *p; unsigned p1; int diff = 0; /* make a buffer for the new length */ sprintf(lenbuf,"%d",newlength); lenbuf_len = strlen(lenbuf); /* this is our current buffer - do we need this? */ skb = *skb_p; iph = skb->nh.iph; uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]); data = (char *)&uh[1]; /* We can search the packet up until here, being overly safe for now */ data_limit = skb->h.raw + skb->len - 18; /* mod up the contact */ while (data < data_limit) { /* find the topmost tag */ if (strnicmp(data,"\ncontent-length:",16) && strnicmp(data,"\nl:",3) && strnicmp(data,"\rcontent-length:",16) && strnicmp(data,"\rm:",3)) { data++; continue; } IP_MASQ_DEBUG(1-debug, "masq_sip: Found content-length header\n"); while (*data!=':') { if (data == data_limit) break; data++; } data++; while (*data==' ') { if (data == data_limit) break; data++; } p = data; p1 = simple_strtoul(data,&data,20); *skb_p = ip_masq_skb_replace(skb, GFP_ATOMIC, p, data-p, lenbuf, lenbuf_len); /* we're supposed to return this */ diff += (lenbuf_len - (data-p)); IP_MASQ_DEBUG(1-debug, "masq_sip: Content-length tag replaced\n"); break; } return diff; } /** * check incomming sdp for our address and change it to be the addr of the inside host */ int masq_sip_mangle_sdp_in (struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr) { struct sk_buff *skb; struct iphdr *iph; struct udphdr *uh; struct ip_masq *n_ms; char *data; char *data_limit; char *addrstart; char *portstart; char portbuf[7]; unsigned portbuf_len; unsigned port = 0; int diff = 0; __u32 useaddr; unsigned char p1,p2,p3,p4; unsigned portnum; __u32 from = 0; char ourbuf[32]; unsigned ourbuf_len; if( emaddr ) useaddr = emaddr; else useaddr = maddr; /* this is our current buffer - do we need this? */ skb = *skb_p; iph = skb->nh.iph; uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]); data = (char *)&uh[1] + masq_sip_find_body_start((char *)&uh[1], skb->len); /* We can search the packet up until here, being overly safe for now */ data_limit = skb->h.raw + skb->len - 4; /* first we find the c= line to get the ip addr */ while (data < data_limit) { /* find the c= line */ if (strncmp(data,"\nc=",3) && strncmp(data,"\rc=",3)) { data++; continue; } data += 3; IP_MASQ_DEBUG(1-debug, "masq_sip: Found SDP c= line\n"); /* make sure it's something we can masq */ if (strncmp(data,"IN IP4 ",7)) continue; data += 7; /* if it's the null address, then the call is on hold, no mangle required regardless */ if (!strncmp(data,"0.0.0.0",7)) { IP_MASQ_DEBUG(1-debug, "masq_sip: Null address found in SDP message\n"); return 0; } p1 = simple_strtoul(data,&data,10); if (*data!='.') IP_MASQ_DEBUG(1-debug, "masq_sip: malformed c= line\n"); p2 = simple_strtoul(data+1,&data,10); if (*data!='.') IP_MASQ_DEBUG(1-debug, "masq_sip: malformed c= line\n"); p3 = simple_strtoul(data+1,&data,10); if (*data!='.') IP_MASQ_DEBUG(1-debug, "masq_sip: malformed c= line\n"); p4 = simple_strtoul(data+1,&data,10); from = (p1<<24) | (p2<<16) | (p3<<8) | p4; break; } if (useaddr != htonl(from)) { return 0; } data = (char *)&uh[1] + masq_sip_find_body_start((char *)&uh[1], skb->len); /* now we find the m= line to check the port */ while (data < data_limit) { /* find the m= line */ if (strncmp(data,"\nm=",3) && strncmp(data,"\rm=",3)) { data++; continue; } data += 3; /* make sure it's an audio stream */ if (strncmp(data,"audio ",6)) continue; data += 6; portstart = data; port = simple_strtoul(data,&data,10); break; } /* lookup the masq entry for this port, if one exists */ n_ms = ip_masq_in_get(IPPROTO_UDP, 0, 0, maddr, htons(port)); if (n_ms == NULL) { IP_MASQ_DEBUG(1-debug, "masq_sip: Local addr found in SDP, but no masq entry exists! Ignoring\n"); return 0; } else { /* save our address without port in ouraddrnoport */ sprintf(ourbuf,"%d.%d.%d.%d",NIPQUAD(n_ms->saddr)); ourbuf_len = strlen(ourbuf); portnum = ntohs(n_ms->sport); } data = (char *)&uh[1] + masq_sip_find_body_start((char *)&uh[1], skb->len); /* We can search the packet up until here, being overly safe for now */ data_limit = skb->h.raw + skb->len - 4; /* first we change the c= line to reflect the masq ip */ while (data < data_limit) { /* find the c= line */ if (strncmp(data,"\nc=",3) && strncmp(data,"\rc=",3)) { data++; continue; } data += 3; IP_MASQ_DEBUG(1-debug, "masq_sip: Found SDP c= line\n"); /* make sure it's something we can masq */ if (strncmp(data,"IN IP4 ",7)) continue; data += 7; addrstart = data; /* this accepts FQDNs or dotted quads */ while (ISALPHA(*data) || ISDIGIT(*data) || (*data=='.') || (*data=='-')) { data++; if (data == data_limit) return 0; } *skb_p = ip_masq_skb_replace(skb, GFP_ATOMIC, addrstart, data-addrstart, ourbuf, ourbuf_len); /* we're supposed to return this */ diff += (ourbuf_len - (data-addrstart)); IP_MASQ_DEBUG(1-debug, "masq_sip: c= line replaced\n"); break; } /* this is our current buffer - do we need this? */ skb = *skb_p; iph = skb->nh.iph; uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]); data = (char *)&uh[1] + masq_sip_find_body_start((char *)&uh[1], skb->len); /* We can search the packet up until here, being overly safe for now */ data_limit = skb->h.raw + skb->len - 4; /* now we update the m= line with the new port */ while (data < data_limit) { /* find the m= line */ if (strncmp(data,"\nm=",3) && strncmp(data,"\rm=",3)) { data++; continue; } data += 3; /* make sure it's something we can masq */ if (strncmp(data,"audio ",6)) continue; data += 6; portstart = data; port = simple_strtoul(data,&data,10); sprintf(portbuf,"%d",portnum); portbuf_len = strlen(portbuf); *skb_p = ip_masq_skb_replace(skb, GFP_ATOMIC, portstart, data-portstart, portbuf, portbuf_len); /* we're supposed to return this */ diff += (portbuf_len - (data-portstart)); IP_MASQ_DEBUG(1-debug, "masq_sip: m= line replaced\n"); break; } return diff; } /** * create a masq entry and change the sdp to reflect it */ int masq_sip_mangle_sdp_out (struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr, char *buf, unsigned buf_len) { struct sk_buff *skb; struct iphdr *iph; struct udphdr *uh; struct ip_masq *n_ms; char *data; char *data_limit; char *addrstart; char *portstart; char portbuf[7]; unsigned portbuf_len; unsigned port; unsigned char p1,p2,p3,p4; __u32 rtpaddr = ms->saddr; int diff = 0; /* this is our current buffer - do we need this? */ skb = *skb_p; iph = skb->nh.iph; uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]); data = (char *)&uh[1] + masq_sip_find_body_start((char *)&uh[1], skb->len); /* We can search the packet up until here, being overly safe for now */ data_limit = skb->h.raw + skb->len - 4; /* first we find the c= line to grab the ip of where we send audio */ while (data < data_limit) { /* find the c= line */ if (strncmp(data,"\nc=",3) && strncmp(data,"\rc=",3)) { data++; continue; } data += 3; IP_MASQ_DEBUG(1-debug, "masq_sip: Found SDP c= line\n"); /* make sure it's something we can masq */ if (strncmp(data,"IN IP4 ",7)) continue; data += 7; /* if it's the null address, then the call is on hold, no mangle required */ if (!strncmp(data,"0.0.0.0",7)) { IP_MASQ_DEBUG(1-debug, "masq_sip: Null address found in SDP message\n"); return 0; } p1 = simple_strtoul(data,&data,10); if (*data!='.') { IP_MASQ_DEBUG(1-debug, "masq_sip: c= is not an ip address, ignoring\n"); break; } p2 = simple_strtoul(data+1,&data,10); if (*data!='.') { IP_MASQ_DEBUG(1-debug, "masq_sip: c= is not an ip address, ignoring\n"); break; } p3 = simple_strtoul(data+1,&data,10); if (*data!='.') { IP_MASQ_DEBUG(1-debug, "masq_sip: c= is not an ip address, ignoring\n"); break; } p4 = simple_strtoul(data+1,&data,10); rtpaddr = (p1<<24) | (p2<<16) | (p3<<8) | p4; rtpaddr = htonl(rtpaddr); IP_MASQ_DEBUG(1-debug, "masq_sip: c= address grabbed\n"); break; } /* this is our current buffer - do we need this? */ skb = *skb_p; iph = skb->nh.iph; uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]); data = (char *)&uh[1] + masq_sip_find_body_start((char *)&uh[1], skb->len); /* We can search the packet up until here, being overly safe for now */ data_limit = skb->h.raw + skb->len - 4; /* then we change the c= line to reflect the masq ip */ while (data < data_limit) { /* find the c= line */ if (strncmp(data,"\nc=",3) && strncmp(data,"\rc=",3)) { data++; continue; } data += 3; IP_MASQ_DEBUG(1-debug, "masq_sip: Found SDP c= line\n"); /* make sure it's something we can masq */ if (strncmp(data,"IN IP4 ",7)) continue; data += 7; /* if it's the null address, then the call is on hold, no mangle required */ if (!strncmp(data,"0.0.0.0",7)) { IP_MASQ_DEBUG(1-debug, "masq_sip: Null address found in SDP message\n"); return 0; } addrstart = data; /* this accepts FQDNs or dotted quads */ while (ISALPHA(*data) || ISDIGIT(*data) || (*data=='.') || (*data=='-')) { data++; if (data == data_limit) return 0; } *skb_p = ip_masq_skb_replace(skb, GFP_ATOMIC, addrstart, data-addrstart, buf, buf_len); /* we're supposed to return this */ diff += (buf_len - (data-addrstart)); IP_MASQ_DEBUG(1-debug, "masq_sip: c= line replaced\n"); break; } /* this is our current buffer - do we need this? */ skb = *skb_p; iph = skb->nh.iph; uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]); data = (char *)&uh[1] + masq_sip_find_body_start((char *)&uh[1], skb->len); /* We can search the packet up until here, being overly safe for now */ data_limit = skb->h.raw + skb->len - 4; /* now we update the m= line with the new port */ while (data < data_limit) { /* find the m= line */ if (strncmp(data,"\nm=",3) && strncmp(data,"\rm=",3)) { data++; continue; } data += 3; /* make sure it's something we can masq */ if (strncmp(data,"audio ",6)) continue; data += 6; portstart = data; port = simple_strtoul(data,&data,10); IP_MASQ_DEBUG(1-debug, "masq_sip: Found port %d in SDP message\n",port); IP_MASQ_DEBUG(1-debug, "masq_sip: checking for masq entry\n"); n_ms = ip_masq_out_get(IPPROTO_UDP, rtpaddr, htons(port), 0, 0); if (n_ms == NULL) { IP_MASQ_DEBUG(1-debug, "masq_sip: no entry found, creating\n"); n_ms = ip_masq_new( IPPROTO_UDP, maddr, 0, rtpaddr, htons(port), 0, 0, IP_MASQ_F_NO_DADDR | IP_MASQ_F_NO_DPORT); if (n_ms == NULL) { IP_MASQ_DEBUG(1-debug, "masq_sip: ip_masq_new returned NULL\n"); IP_MASQ_DEBUG(1-debug, "masq_sip: bailing on outgoing SDP\n"); return diff; } ip_masq_listen(n_ms); ip_masq_control_add(n_ms, ms); ip_masq_put(n_ms); } IP_MASQ_DEBUG(1-debug, "masq_sip: forwarding incomming rtp on port %d to %d.%d.%d.%d:%d\n",ntohs(n_ms->mport),NIPQUAD(rtpaddr),port); sprintf(portbuf,"%d",ntohs(n_ms->mport)); portbuf_len = strlen(portbuf); *skb_p = ip_masq_skb_replace(skb, GFP_ATOMIC, portstart, data-portstart, portbuf, portbuf_len); /* we're supposed to return this */ diff += (portbuf_len - (data-portstart)); IP_MASQ_DEBUG(1-debug, "masq_sip: m= line replaced\n"); break; } return diff; } int masq_sip_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr) { return masq_sip_mangle_sdp_in(ms, skb_p, maddr); } int masq_sip_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr) { struct sk_buff *skb; struct iphdr *iph; struct udphdr *uh; struct ip_masq *n_ms; char *data; char ouraddr[32]; /* xxx.xxx.xxx.xxx:xxxxx\000 */ unsigned ouraddr_len; char ouraddrnoport[32]; unsigned ouraddrnoport_len; int diff = 0; __u32 useaddr; if( emaddr ) useaddr = emaddr; else useaddr = maddr; /* setup the control connection to have source port 5060, and make sure * we use that */ n_ms = ip_masq_out_get( IPPROTO_UDP, ms->saddr, htons( 5060 ), 0, 0); if (n_ms != NULL) { ms = n_ms; } else { ms->sport = htons( 5060 ); } /* setup the control connection with a new timeout table */ if (!ms->timeout_table) ms->timeout_table = &sip_timeout_table; IP_MASQ_DEBUG(1-debug, "masq_sip: Message output\n"); /* save our address in ouraddr */ sprintf(ouraddr,"%d.%d.%d.%d:%d",NIPQUAD(useaddr),ntohs(ms->mport)); ouraddr_len = strlen(ouraddr); /* save our address without port in ouraddrnoport */ sprintf(ouraddrnoport,"%d.%d.%d.%d",NIPQUAD(useaddr)); ouraddrnoport_len = strlen(ouraddrnoport); /* this is our current buffer */ skb = *skb_p; iph = skb->nh.iph; uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]); /* lets keep the original around please */ data = (char *)&uh[1]; if (strncmp(data,"SIP/2.0",7)) { /* message is a request */ /* so, mangle the via tag */ diff += masq_sip_mangle_via(skb_p, ouraddr, ouraddr_len); } /* mangle the contact tag */ diff += masq_sip_mangle_contact(skb_p, ouraddr, ouraddr_len); /* mangle the sdp body (if there is one) */ diff += masq_sip_mangle_sdp_out(ms, skb_p, maddr, ouraddrnoport, ouraddrnoport_len); /* redo the content length */ skb = *skb_p; diff += masq_sip_mangle_content_length(skb_p, (skb->len - masq_sip_find_body_start(skb->h.raw, skb->len))); /* we might not even have to do this */ skb = *skb_p; iph = skb->nh.iph; uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]); /* update udp header */ uh->check = 0; uh->len = htons(ntohs(uh->len) + diff); /* update the checksums */ ip_send_check(iph); return diff; } struct ip_masq_app ip_masq_sip = { NULL, /* next */ "sip", /* name */ 0, /* type */ 0, /* n_attach */ masq_sip_init_1, /* ip_masq_init_1 */ masq_sip_done_1, /* ip_masq_done_1 */ masq_sip_out, /* pkt_out */ masq_sip_in, /* pkt_in */ }; /* * ip_masq_sip initialization */ __initfunc(int ip_masq_sip_init(void)) { int i, j; int valid = 1; char *data; unsigned char p1,p2,p3,p4; emaddr = 0; if (ipaddr) { data = ipaddr; p1 = simple_strtoul(data,&data,10); if (*data!='.') { IP_MASQ_DEBUG(1-debug, "masq_sip: malformed ipaddr option, ignoring\n"); valid = 0; } p2 = simple_strtoul(data+1,&data,10); if (*data!='.') { IP_MASQ_DEBUG(1-debug, "masq_sip: malformed ipaddr option, ignoring\n"); valid = 0; } p3 = simple_strtoul(data+1,&data,10); if (*data!='.') { IP_MASQ_DEBUG(1-debug, "masq_sip: malformed ipaddr option, ignoring\n"); valid = 0; } p4 = simple_strtoul(data+1,&data,10); if( valid ) { emaddr = (p1<<24) | (p2<<16) | (p3<<8) | p4; emaddr = htonl(emaddr); } } for (i=0; i