程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C >> 關於C >> 計算 ip udp tcp 效驗和

計算 ip udp tcp 效驗和

編輯:關於C

下面敘述的是IPv4的 ip udp tcp 效驗和的計算。
ip效驗和計算相對簡單,只需對ip協議頭進行計算;
ip協議頭長20字節;
udp協議頭長8字節;
tcp協議頭長20~60字節;
udp和tcp的校驗和不僅要對整個ip協議負載(包括
udp/tcp協議頭和udp/tcp協議負載)進行計算,
還要先對一個偽協議頭進行計算。
在計算效驗和前,協議頭效驗和字段要清0。

偽協議頭結構如下:
  0     7 8      15 16     23 24  31
  +--------+--------+--------+--------+
  |           source address            |
  +--------+--------+--------+--------+
  |        destination address        |
  +--------+--------+--------+--------+
  | zero   |protocol| udp/tcp length |
  +--------+--------+--------+--------+

偽協議頭中的字段同樣都是網絡字節序;


下面代碼片段展現的是在GNU/Linux中具體的計算方式:

//=====================================================
#include <stdint.h>

#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ether.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>

struct pseudo_head {
    uint32_t saddr;
    uint32_t daddr;
    char zero;
    char proto;
    unsigned short len;
};

int cal_cksum(char *p)
{
    struct iphdr *ip = (struct iphdr *)
        (p + sizeof(struct ether_header));

    cal_ip_cksum(ip);

    struct tcphdr *tcp;
    struct udphdr *udp;
    char *f = (char *)ip + 20;

    switch(ip->protocol) {
    case IPPROTO_TCP:
        tcp = (struct tcphdr *)f;
        tcp->check = 0;
        tcp->check = cal_udptcp_cksum(
            (unsigned short *)f, ip);
        break;
    case IPPROTO_UDP:
        udp = (struct udphdr *)f;
        udp->check = 0;
        udp->check = cal_udptcp_cksum(
            (unsigned short *)f, ip);
        break;
    case IPPROTO_IP:
        break;
    default:
        return 1;
    }
    return 0;
}

inline static void cal_ip_cksum(struct iphdr *ip)
{
    ip->check = 0;
    ip->check = in_cksum((unsigned short *)ip,
            20, 0);
}

inline static unsigned short cal_udptcp_cksum(
        unsigned short *p, struct iphdr *ip)
{
    int len = ntohs(ip->tot_len) - 20;
    struct pseudo_head ph;

    ph.len = htons(len);
    ph.saddr = ip->saddr;
    ph.daddr = ip->daddr;
    ph.proto = ip->protocol;
    ph.zero = 0;

    int sum = cal_sum(
        (unsigned short *)&ph, sizeof(ph));

    return in_cksum(p, len, sum);
}

unsigned short in_cksum(unsigned short *p,
        int len, int sum)
{
    int n = len;

    while(n > 1) {
        sum += *p++;
        n -= 2;
    }
    if(n == 1)
        sum += *(unsigned char *)p;

    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);

    unsigned short ret = ~sum;
    return ret;
}

inline static int cal_sum(unsigned short *p, int len)
{
    int sum = 0;
    while(len > 0) {
        sum += *p++;
        len -= 2;
    }
    return sum;
}


摘自 leeshuheng的專欄
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved