00001
00002
00003
00004
00005
00006
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
00042 if ((padlen = 4 - (optlen % 4)) == 4)
00043 padlen = 0;
00044
00045
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
00052 if (IP_OPT_TYPEONLY(((struct ip_opt *)optbuf)->opt_type))
00053 optlen = 1;
00054
00055
00056 if (datalen) {
00057 memmove(p + optlen + padlen, p, datalen);
00058 }
00059
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;
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
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 }