tcp/ip協議學習 第三章 IP協議

派猴子來的救兵html

 

關於IP的RFC文檔在此!python

IP的頭文件仍是先貼一下, 老是記不住.算法

0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |Version|  IHL  |Type of Service|          Total Length         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |         Identification        |Flags|      Fragment Offset    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Time to Live |    Protocol   |         Header Checksum       |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                       Source Address                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Destination Address                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Options                    |    Padding    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

網絡和子網劃分

不太明白ABCD四類地址劃分的意義,除了在路由選擇的匹配中會配合子網劃分用到.ubuntu

路由表, 路由選擇

之前本科的時候, IP配置好以後, 學校內網是能夠訪問的. 若是想訪問外網, 須要撥號.但撥號以後內網就不方便上了.小程序

學校網站有個小程序, 能夠傻瓜式的修改系統路由. 撥號以後內網外網一塊兒上.網絡

當時不明白這是個什麼東西, 其實如今也想不太明白怎麼回事, 也沒條件回學校試驗下了. 但至少如今也算大概明白路由表以及路由選擇了.併發

以前爲了學習, 拿公司的網絡實驗了一把. osx下面的雙網卡路由配置socket

來段程序吧

python

用程序發送raw IP包的時候, Total Length 和 Checksum 會由內核自動生成. 至於爲啥,可能看到tcpip協議詳解下(實現)的時候就知道了吧.tcp

Checksum 算法在模擬後面協議的時候就要本身實現了.ICMP、IGMP、UDP和TCP都採用相同的檢驗和算法.學習

有個不一樣點是: 首部檢驗和字段是根據IP頭計算的檢驗和碼。它不對body進行計算。 ICMP、 IGMP、UDP和TCP的checksum覆蓋首部和數據。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

'''發送一個裸的IP包, 20字節的IP頭, 後面跟一個隨便寫的字符串.
還不知道IP包的ID應該根據什麼生成, 就隨便寫了一個54321
IP頭裏面:
 IP包總長度屬性和checksum屬性都是內核自動生成的.
 協議是用的socket.IPPROTO_TCP,也就是6.但沒什麼用,IP包裏面就隨便的字符串,不是按TCP協議來的.
'''

import socket
from struct import pack
import sys


def main():
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
    except socket.error as msg:
        print 'Socket could not be created. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
        return

    packet = ''

    source_ip = '127.0.0.1'
    dest_ip = '127.0.0.1'

    # ip header fields
    ip_ver = 4
    ip_ihl = 5
    ip_tos = 0
    ip_tot_len = 0  # kernel will fill the correct total length
    ip_id = 54321
    ip_frag_off = 0
    ip_ttl = 32
    ip_proto = socket.IPPROTO_TCP  # no use in this case
    ip_checksum = 0    # kernel will fill the correct checksum

    ip_saddr = socket.inet_aton(source_ip)
    ip_daddr = socket.inet_aton(dest_ip)

    ip_ihl_ver = (ip_ver << 4) + ip_ihl

    # the ! in the pack format string means network order
    ip_header = pack(
        '!BBHHHBBH4s4s',
        ip_ihl_ver,
        ip_tos,
        ip_tot_len,
        ip_id,
        ip_frag_off,
        ip_ttl,
        ip_proto,
        ip_checksum,
        ip_saddr,
        ip_daddr)

    user_data = sys.argv[1] if sys.argv[1:] else '0123456789'

    packet = ip_header + user_data

    # the port specified has no effect
    r = s.sendto(packet, (dest_ip, 0))

    # result is the length of packet sent out
    print r

if __name__ == '__main__':
    main()
    ip_check = 1231    # kernel will fill the correct checksum

抓包. 運行環境, ubuntu12.04

% sudo tcpdump -i lo -x -vvv -t -c 1                                                                                                ✭
tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 65535 bytes
IP (tos 0x0, ttl 32, id 54321, offset 0, flags [none], proto TCP (6), length 30)
    localhost.12337 > localhost.12851:  tcp 10 [bad hdr length 0 - too short, < 20]
    0x0000:  4500 001e d431 0000 2006 c8a6 7f00 0001
    0x0010:  7f00 0001 3031 3233 3435 3637 3839
1 packet captured
2 packets received by filter
0 packets dropped by kernel

tcp 10 [bad hdr length 0 - too short, < 20] 是說IP body裏面的字節數太少, 只有10, 小於TCP header應有的20字節.

  • 1字節, 45, 4表明Version, 5表明Header Length, 單位是4Byte.

  • 2字節, 00, OTS(Type of Service), 表明此IP包的最小時延,最大吞吐量,最高可靠性, 最小費用等特徵. 好比用於FTP的控制的IP包, 應該是最小時延, 用於FTP的數據的IP包, 則應該是最大吞吐量.書中提到:

    如今大多數的TCP/IP實現都不支持TOS特性,可是自4.3BSD Reno之後的新版系統都對它 進行了設置。另外,新的路由協議如 OSPF和IS-IS都能根據這些字段的值進行路由決策。

  • 3,4字節, 001e, Total Length. 0x1e = 30; 發送的01234567890+20字節的IP Header.
    由於一些數據鏈路(如以太網)須要填充一些數據以達到最小長度。儘管以太網的最小幀長爲 46字節,可是IP數據可能會更短,像咱們就是30。若是沒有總長度字段,那麼IP層就不知道46字節中有多少是IP數據報的內容。因此IP包總長度這個字段是必定須要的.

  • 5,6字節, Identification. 0xd431=54321.

  • 7,8字節, 0000.

  • 9字節, 20. TTL. 路由轉發此IP包的時候就把這個數值減1, 減到0的時候就直接丟棄, 併發送ICMP通知主機. trouceroute即利用了這個.

  • 10字節, 06, 表明TCP協議.

  • 11,12字節 c8a6. Checksum.

  • 13-16字節, 7f00 0001. Source Address. 表明127.0.0.1

  • 17-20字節, 7f00 0001. Destination Address. 表明127.0.0.1

Checksum是內核幫咱們算好的, 但之後的TCP等就要本身算了, 這裏先看一下算法.

首先把檢驗和字段置爲 0。而後,對首部中每一個 16 bit 進行二進制反碼求和(整個首部當作是由一串 16 bit的字組成)

def checksum(ip_header):
    ip_header += chr(0x00)*(len(ip_header)%2)
    r = 0
    while ip_header:
        # 由於ip_header已是網絡序了, 因此這裏用!H. 不然須要用H
        r += unpack('H',ip_header[:2])[0]
        ip_header = ip_header[2:]

    if r > 0xffff:
        r = r&0xffff + (r>>16);

    return ~r
相關文章
相關標籤/搜索