Main Page | Modules | Class List | Directories | File List | Class Members | File Members | Related Pages

ip-util.c

Go to the documentation of this file.
00001 /*
00002  * ip-util.c
00003  *
00004  * Copyright (c) 2002 Dug Song <dugsong@monkey.org>
00005  *
00006  * $Id: ip-util.c,v 1.9 2005/02/17 02:55:56 dugsong Exp $
00007  */
00008 
00009 #include "config.h"
00010 
00011 #include <errno.h>
00012 #include <stdlib.h>
00013 #include <string.h>
00014 
00015 #include "dnet.h"
00016 
00017 ssize_t
00018 ip_add_option(void *buf, size_t len, int proto,
00019     const void *optbuf, size_t optlen)
00020 {
00021         struct ip_hdr *ip;
00022         struct tcp_hdr *tcp = NULL;
00023         u_char *p;
00024         int hl, datalen, padlen;
00025         
00026         if (proto != IP_PROTO_IP && proto != IP_PROTO_TCP) {
00027                 errno = EINVAL;
00028                 return (-1);
00029         }
00030         ip = (struct ip_hdr *)buf;
00031         hl = ip->ip_hl << 2;
00032         p = (u_char *)buf + hl;
00033         
00034         if (proto == IP_PROTO_TCP) {
00035                 tcp = (struct tcp_hdr *)p;
00036                 hl = tcp->th_off << 2;
00037                 p = (u_char *)tcp + hl;
00038         }
00039         datalen = ntohs(ip->ip_len) - (p - (u_char *)buf);
00040         
00041         /* Compute padding to next word boundary. */
00042         if ((padlen = 4 - (optlen % 4)) == 4)
00043                 padlen = 0;
00044 
00045         /* XXX - IP_HDR_LEN_MAX == TCP_HDR_LEN_MAX */
00046         if (hl + optlen + padlen > IP_HDR_LEN_MAX ||
00047             ntohs(ip->ip_len) + optlen + padlen > len) {
00048                 errno = EINVAL;
00049                 return (-1);
00050         }
00051         /* XXX - IP_OPT_TYPEONLY() == TCP_OPT_TYPEONLY */
00052         if (IP_OPT_TYPEONLY(((struct ip_opt *)optbuf)->opt_type))
00053                 optlen = 1;
00054         
00055         /* Shift any existing data. */
00056         if (datalen) {
00057                 memmove(p + optlen + padlen, p, datalen);
00058         }
00059         /* XXX - IP_OPT_NOP == TCP_OPT_NOP */
00060         if (padlen) {
00061                 memset(p, IP_OPT_NOP, padlen);
00062                 p += padlen;
00063         }
00064         memmove(p, optbuf, optlen);
00065         p += optlen;
00066         optlen += padlen;
00067         
00068         if (proto == IP_PROTO_IP)
00069                 ip->ip_hl = (p - (u_char *)ip) >> 2;
00070         else if (proto == IP_PROTO_TCP)
00071                 tcp->th_off = (p - (u_char *)tcp) >> 2;
00072 
00073         ip->ip_len = htons(ntohs(ip->ip_len) + optlen);
00074         
00075         return (optlen);
00076 }
00077 
00078 void
00079 ip_checksum(void *buf, size_t len)
00080 {
00081         struct ip_hdr *ip;
00082         int hl, off, sum;
00083 
00084         if (len < IP_HDR_LEN)
00085                 return;
00086         
00087         ip = (struct ip_hdr *)buf;
00088         hl = ip->ip_hl << 2;
00089         ip->ip_sum = 0;
00090         sum = ip_cksum_add(ip, hl, 0);
00091         ip->ip_sum = ip_cksum_carry(sum);
00092 
00093         off = htons(ip->ip_off);
00094         
00095         if ((off & IP_OFFMASK) != 0 || (off & IP_MF) != 0)
00096                 return;
00097         
00098         len -= hl;
00099         
00100         if (ip->ip_p == IP_PROTO_TCP) {
00101                 struct tcp_hdr *tcp = (struct tcp_hdr *)((u_char *)ip + hl);
00102                 
00103                 if (len >= TCP_HDR_LEN) {
00104                         tcp->th_sum = 0;
00105                         sum = ip_cksum_add(tcp, len, 0) +
00106                             htons(ip->ip_p + len);
00107                         sum = ip_cksum_add(&ip->ip_src, 8, sum);
00108                         tcp->th_sum = ip_cksum_carry(sum);
00109                 }
00110         } else if (ip->ip_p == IP_PROTO_UDP) {
00111                 struct udp_hdr *udp = (struct udp_hdr *)((u_char *)ip + hl);
00112 
00113                 if (len >= UDP_HDR_LEN) {
00114                         udp->uh_sum = 0;
00115                         sum = ip_cksum_add(udp, len, 0) +
00116                             htons(ip->ip_p + len);
00117                         sum = ip_cksum_add(&ip->ip_src, 8, sum);
00118                         udp->uh_sum = ip_cksum_carry(sum);
00119                         if (!udp->uh_sum)
00120                                 udp->uh_sum = 0xffff;   /* RFC 768 */
00121                 }
00122         } else if (ip->ip_p == IP_PROTO_ICMP || ip->ip_p == IP_PROTO_IGMP) {
00123                 struct icmp_hdr *icmp = (struct icmp_hdr *)((u_char *)ip + hl);
00124                 
00125                 if (len >= ICMP_HDR_LEN) {
00126                         icmp->icmp_cksum = 0;
00127                         sum = ip_cksum_add(icmp, len, 0);
00128                         icmp->icmp_cksum = ip_cksum_carry(sum);
00129                 }
00130         }
00131 }
00132 
00133 int
00134 ip_cksum_add(const void *buf, size_t len, int cksum)
00135 {
00136         uint16_t *sp = (uint16_t *)buf;
00137         int n, sn;
00138         
00139         sn = len / 2;
00140         n = (sn + 15) / 16;
00141 
00142         /* XXX - unroll loop using Duff's device. */
00143         switch (sn % 16) {
00144         case 0: do {
00145                 cksum += *sp++;
00146         case 15:
00147                 cksum += *sp++;
00148         case 14:
00149                 cksum += *sp++;
00150         case 13:
00151                 cksum += *sp++;
00152         case 12:
00153                 cksum += *sp++;
00154         case 11:
00155                 cksum += *sp++;
00156         case 10:
00157                 cksum += *sp++;
00158         case 9:
00159                 cksum += *sp++;
00160         case 8:
00161                 cksum += *sp++;
00162         case 7:
00163                 cksum += *sp++;
00164         case 6:
00165                 cksum += *sp++;
00166         case 5:
00167                 cksum += *sp++;
00168         case 4:
00169                 cksum += *sp++;
00170         case 3:
00171                 cksum += *sp++;
00172         case 2:
00173                 cksum += *sp++;
00174         case 1:
00175                 cksum += *sp++;
00176                 } while (--n > 0);
00177         }
00178         if (len & 1)
00179                 cksum += htons(*(u_char *)sp << 8);
00180 
00181         return (cksum);
00182 }

Generated on Sun May 14 14:51:12 2006 by  doxygen 1.4.2