오늘은 지금까지 분석해왔던 패킷을 직접 만들어볼 것이다.

오늘은 L2 계층의 패킷을 만들어볼 것이다.

대표적인 ARP 패킷을 만들것이다.

ARP패킷에 들어가는 내용은
이더넷 헤더(14바이트), ARP 헤더(28바이트) -> 총 42바이트가 들어간다.

먼저
패킷을 만드는 코드를 짜야된다.


(패킷 생성 코드)



가운데 주석 표시해 둔 곳에
내용에 맞는 패킷을 작성하면 된다.


(패킷 내용을 작성한 코드)



(실행해서 덤프받은 화면)



위 화면은 ARP 패킷을 전송한 것을 덤프받아서 확인한 결과이다.

아래 바로 ARP 응답 패킷또한 확인 할 수 있다.
ARP 응답은 Operation이 2이다.


ARP 패킷을 만들어서 전송하면서 발견한 취약점.

* ARP 패킷을 받으면 그 받은 host는 그 내용을 무조건 신뢰하며
그 데이터에 따라 자기 캐쉬 테이블을 업데이트한다.

우리가 패킷 내용을 작성해서 보냈지만,
솔직하게 데이터를 쓰지 않을 수도 있는것 아닐까?
그렇게 되면 상대 host의 ARP 테이블은 어떻게 될까?



(나의 맥주소를 77:77:77:77:77:77 이라고 속인 코드)



이렇게 ARP 패킷을 스푸핑했다.
스푸핑 -> 속이다.

옆 사람이 내 컴퓨터로 ARP 패킷을 조작해서 보낸 결과는 다음과 같다.



(아래에 192.168.3.239 주소 맥주소)



192.168.3.239의 맥주소를 보면 이상하다.
aa-bb-cc-dd-ee-ff 라니?...

게다가 하면서 더 요상한걸 발견했다.

ARP 패킷을 보내는데
IP주소도 변조해서 보낸다면?...



(엉망이된 나의 ARP 테이블)



위 화면을 보면 요상한 맥주소들이 등록되어있다.
이 경우 아주 착하게도 맥주소가 잘못된것을 바로 알기 쉽지만
보통 일반적인쓰레기맥주소를 입력한다면 구별하기 힘들것이다.

또 이로 인해 네트워크 통신에 문제가 생길 수 있다.

바로 이렇게 ARP 테이블을 망쳐놓는다 해서
이 공격이름은 ARP Cache Poisoning 이라고 한다.





* NAT  환경
NAT 안쪽 : Interner
NAT 밖쪽 : Externer

게이트웨이 : 외부망과 내부망을 연결해주는 라우터이다.

게이트웨이에서 내부망으로 연결되는 사설 ip는
 보통 끝자리가 1 or 254이다.
(주로 그렇다는것이다. 꼭그런건 아니다.)

같은 네트워크에 있지 않으면 맥주소를 알 수 없다.
(같은 네트워크에 있다면  ARP를 통해 맥주소를 알 수 있다.)
이유는

ARP 패킷은 IP 패킷안에 패키징 되지 않기 때문에
라우터를 넘어가지 못한다.
그렇기 때문에 ARP는 같은 네트워크 안에서만 동작한다.

내부망에서는 MAC주소로 통신을 하고
외부망과의 통신은 IP주소만 가지고 통신을 한다.


내부망에서 어떻게 상대방의 맥주소를 알 수 있을까?
-ARP ( Address Resolution Protocol)
내부망에서 상대방의 맥주소를 알아야하는 이유는 게이트웨이까지 통신이 되야하기 때문이다.

1. ARP Request
        - Broadcast

2. ARP Reply



일단,
ARP 패킷을 한번 직접 받아서 분석해보겠다.

먼저 패킷을 덤프받는 코드를 조금 수정하였다.
어제까지 L2의 이더넷 헤더를 덤프받을 수 있었으므로
이더넷 헤더에서 ARP 패킷을 구분할 수 있다.

(마지막 부분에 ARP 표시한 코드)

 

 

 

(덤프받은 ARP 패킷 내용)

 

 

ARP 패킷 받은것
['0x0', '0x1',  '0x8', '0x0',  '0x6', '0x4',  '0x0', '0x1',
'0x90', '0x9f', '0x33', '0xeb','0x3b', '0x42',
'0xc0', '0xa8', '0x3', '0x60',
'0x0', '0x0', '0x0', '0x0', '0x0', '0x0',
'0xc0', '0xa8', '0x3', '0x67',
 '0x0', '0x0', '0x0',
 '0x0', '0x0', '0x0', '0x0', '0x0', '0x0', '0x0', '0x0', '0x0',
'0x0', '0x0', '0x0', '0x0', '0x0', '0x0']

1. Hardware type (2바이트) : '0x0', '0x1 = 이더넷인터페이스
2. Protocol type (2바이트) : '0x8', '0x0' = 0x0800 = IPv4
3. Hardware length (1바이트) : '0x6'
4. Protocol length (1바이트) : '0x4'
5. Operation (2바이트) : '0x0', '0x1 = 1 : Request
                                                              2 : Reply
6. sender Mac Address (6바이트) : '0x90', '0x9f', '0x33', '0xeb','0x3b', '0x42'
7. sender IP Address (4바이트) : '0xc0', '0xa8', '0x3', '0x60', = 192.168.3.96
8. Target Mac Address (6바이트) : '0x0', '0x0', '0x0', '0x0', '0x0', '0x0',
     -> 모르니까 0으로 비워서 보내는 것이다.
9. Target IP Address (4바이트) : '0xc0', '0xa8', '0x3', '0x67'

그 뒤 0들은 프레임맞추기위해 집어넣은것이다.
-> 윈도우즈는 정기적으로 주기적으로 주변 host들에게 맥주소를 물어보는 작업을 한다. (3분? 정도마다)
  그리고 ARP 캐쉬테이블에 넣어둔다.
   커맨드 창에서 arp -a   로 확인할 수 있다.

 

 

 

(윈도우즈 ARP 캐쉬 테이블을 확인할수있다.)

 

 

다음주부터는 패킷을 직접 만들어볼것이다.

* 팁
 집에 갑자기 인터넷이 안될때 검사
1계층 - 잘 꼽혀있는지
2계층 - 나랑 게이트웨이가 통신이 잘되는지 확인
 - 게이트주소에 ping
게트웨이까지는 잘가는데 인터넷이 안되면
게이트웨이의 포워딩이 문제거나
통신사업자에서의 문제거나
3계층...
핑까지 서버에 잘 되는데 웹툰이 안보일때.
-> 이 때는 DNS 문제일 가능성도 있다.  nsloopup 이 안되는 경우가 있다.


또 오늘은 패킷을 보기 쉽게 한번
디자인해보았다.
코드를..

 

 

 

 (깔끔하게 구성된 화면)

Layer 1 - 물리적인 전송을 담당한다.
            - 전기 신호
 ex) 허브에 스타토폴로지로 host들이 연결되있다.
 한 host에서 전기신호를 보내고 그 전기신호를 수신하여서 데이터를 주고받는다.

Layer 2 - 데이터 링크 계층
 
 - 터널링관련 프로토콜 : PPP, L2TP, ...
 - 라우팅이 필요하지 않는 계층
 - 라우팅없이도 통신이 가능하다.
- 이더넷, 토큰링방식
 토큰링 방식 (토큰을 가진사람만 보낼 수 있고, 그 토큰은 네트워크를 돌아다닌다., 충돌을 예방하는 방법이다.)
  - 단점 : 토큰이 돌아서 내 차례가 될때까지가 오래걸린다.
 이더넷 : Collison Domain (충돌을 이용한다.)
 이더넷 헤더 (고정 : 14바이트)
 -우리는 보통 이더넷을 사용한다.

* 각 계층별 주소
L4 - Port Number
L3 - IP Address
L2 - MAC 주소. (Media Access Control) : 하드웨어 주소
 - 16진수 6자리  (유일한 주소)

 L2까지 합하면 패킷의 구성은
 
   - 이더넷 + IP + TCP/UDP + 데이터

지금까지 IP, TCP/UDP, 데이터까지 보았다.
 
기존에 만들었던건 윈도우즈 소켓을 이용해 패킷을 덤프했던 것이다.
이 윈도우즈소켓으로는 L3까지밖에 확인이 안된다.
그래서
L2까지 보기위해 다른 방법을 사용해보겠다.

새로 만든 모듈을 이용했다.



(모듈로 설정한 get_device)





(실행화면)



어떤 장치로 들어오고 나가는 내용을 화면에 출력할지를 정해야한다.
화면에서 Microsoft 라고 나온것이 무선어댑터 지금 사용하는 것이다.

 실행하면 인터페이스를 선택하라고 나온다.
이 때 여기서 나오는 인터페이스란

제어판에서 네트워크 정보에갔을 때 나오는 목록이다.
이 목록은 컴퓨터의 네트워크 장치이다.
이제 이 장치로부터 패킷을 가져와 열어보겠다는 것이다.





(두개의 어댑터)



이 화면은 집마다 다르다.

여기서 전에 소켓통신했던것과 유사하게
핸들러(장치를 다룰) 를 선언했다. 소켓과 비슷한 역할이다.

그 후 전에 덤프 프로그램과 비슷하게 작성해준다.



(패킷을 덤프받아 출력하는 코드)



이렇게 하면 전에 사용했던 프로그램과 비슷한 모양의 코드가 된다.



(실행 화면)



출력을 해보면 데이터가 출력된다.
캡쳐된 내용을 보면 앞에 14바이트 빼고는 지금까지 봤던 정보들이다.

앞에 14바이트는 이더넷프레임이다.


['0xff', '0xff', '0xff', '0xff', '0xff', '0xff', '0x90', '0x9f', '0x33', '0xeb', '0x34', '0xa1', '0x8', '0x0']

분석해보면
1. 도착지 맥주소(6바이트) :  0xff', '0xff', '0xff', '0xff', '0xff', '0xff
2. 출발지 맥주소(6바이트) : '0x90', '0x9f', '0x33', '0xeb', '0x34', '0xa1'
3. 프로토콜 타입(2바이트) : '0x8', '0x0'
                                          - 0x0800 -> IPv4  , IP 프로토콜
                                            0x8600 -> IPv6
                                            0x0806 -> ARP 프로토콜

통신을 하려면 MAC주소를 알아야한다. 하지만 우리는 IP주소만 알고있다.
그렇기 때문에
ARP 가 필요하다.
ARP : Address Resolution Protocol

이제 전에 작성했던 프로그램에서 L2까지 나오는프로그램으로 확장시켜본다.



(확장 코드)




(출력 화면)


출력 화면을 보면 이제 L2의 프레임 단위로 패킷을 확인할 수 있는것을 볼 수 있다.

이제 내일부터
이 내용을 가지고 네트워크를 살펴보겠다.




어제 IP 주소의 A class 까지 알아보았다.

B class부터 살펴보면

B Class
 - 네트워크 id : 첫번째와 두번재 수를 사용
 - host id : 나머지 두자리를 사용
 - 공통비트 : 10
 
 ex)  1000 0000.0000 0000.hhhh hhhh.hhhh hhhh(2)
      1011 1111.1111.1111.hhhh hhhh.hhhh hhhh(2)
 -10진수로 바꾸면 앞자리가 128이다. (A 클래스가 127까지였고, B클래스는 128부터이다.)
 -128.0.h.h ~ 191.255.h.h (B 클래스 IP주소의 범위)

 ex) 한 네트워크에서 사용 가능한 호스트의 주소
  
  160.120 (네트워크)라면
  
  160.120.0.0 ~ 160.120.255.255
  
- 이 경우 사용 불가능한 주소
  160.120.0.0    -> 160.120 네트워크의 대표주소
  160.120.255.255  -> 160.120. 네트워크 안으로 브로드캐스트 하는 주소
  172 번으로 시작하는 것은 사설IP로 네트워크 ID로 사용할 수 없다.

C Class
 - 네트워크 id : 첫번째, 두번째. 세번째 수를 사용
 - host id : 나머지 한자리를 사용
 - 공통비트 : 110
 1100 0000.0000 0000.0000 0000.hhhh.hhhh(2)
 1101 1111.1111 1111.1111 1111.hhhh.hhhh(2)
 192.0.0.h ~ 223.255.255.h(10) (C 클래스 IP주소의 범위)

 - 사용불가능한 주소는 위의 규칙과 같다.
 - 192 번은 사설 IP로 사용할 수 없다.

D Class
 - 장비나 인터페이스에 설정이 불가능하다.
 - 즉, 사용할 수 없는 IP주소이다.
 
 - 멀티캐스팅 용도로 사용한다.

 - 공통비트 : 1110
 1110 0000.0000 0000.0000 0000.0000 0000
 1110 1111.1111 1111.1111 1111.1111 1111
 224.0.0.0 ~ 239.255.255.255(10) 

E Class
 - 장비나 인터페이스에 설정이 불가능하다.
 - 즉, 사용할 수 없는 IP주소이다.

 - 공통비트 : 1111
 240.0.0.0 ~ 255.255.255.255(10)
 -> 우리는 못쓴다. 예약된 주소로써 사용되지 않는다.

- 통신 방법
유니캐스팅 -> 1:1로 전달하는것이다.
멀티캐스팅 -> 특정 그룹에만 전체 전달하는 것이다.
브로드캐스팅 -> 연결되있는 모든 host에 전달하는 것이다.

위 주소들의 특징을 살펴보면 어떤 통신 방법으로 통신하는지
주소만 보고 알 수 있다.




(목적지 주소가 224.0.0.252)


주소를 보면 D Class에 해당하는 것을 알 수 있다. 이 주소는 멀티캐스팅을 위한 주소이다.
멀티캐스트하는 패킷이라는 것을 알 수 있다.




(192.169.3.255 주소)



이번엔 목적지 주소 끝이 255 이다. 이 주소는 현재 192.168.3.0 이라는 네트워크 id를 가진 네트워크 안에서 브로드캐스트하는 주소이다.

* 넷마스크
 - ip에서 네트워크 아이디를 확인하기 위한 목적이다.
 - 네트워크를 분리한다.(서브네팅)
- 혹은 네트워크를 통합한다.(슈퍼네팅)
 
ex)  192.168.3.100/24      ( /24 ) 가 넷마스크를 의미한다. 넷마스크의 비트수
    -> 24비트를 넷마스크로 쓰겠다는 말이다.

 - 192.168.3.100 / 255.255.255.0  
 -> 2진수로 바꾸면
 1100 0000.1010 1000.0000 0011.0110 0100  
   / 1111 1111.1111 1111.1111 1111.0000 0000 

 Bit Masking 은 and 연산을 통해 이루어진다.

 1100 0000.1010 1000.0000 0011.0110 0100
 1111 1111.1111 1111.1111 1111.0000 0000
 ------------------------------------------------------------
 1100 0000.1010 1000.0000 0011.0000 0000 
-> 10진수로 바꾸면 192.168.3.0 이 나온다.
-> 네트워크 id를 얻을 수 있게된다.
 -> 서브넷마스크의 역할이다.
 - 그래서 이 서브넷마스크를 조절하면 네트워크망의 크기를 조절할 수 있다.(서브넷팅, 슈퍼넷팅)

 -> 만약 의도적으로 서브넷 마스크를 비트 하나 더 늘려주면
 1111 1111.1111 1111.1111 1111.1000 0000  -> 서브넷마스크 25
 192.168.3.100/255.255.255.128
 1100 0000.1010 1000.0000 0011.0110 0100 
        /  1111 1111.1111 1111.1111 1111.1000 0000
 
 ->그렇게되면 두개의 네트워크로 분할 할 수 있고, host의 수는 절반만큼 줄어든다.
 1100 0000.1010 1000.0000 0011.0110 0100
 1111 1111.1111 1111.1111 1111.1000 0000
 ----------------------------------------
 1100 0000.1010 1000.0000 0011.0000 0000   -> 192.168.3.0

만약 
 192.168.3.200/255.255.255.128
 1100 0000.1010 1000.0000 0011.1100 1000
 1111 1111.1111 1111.1111 1111.1000 0000
 ---------------------------------------
 1100 0000.1010 1000.0000 0011.1000 0000  -> 192.168.3.128 이라는 네트워크 id가 된다.
-> 같은 192.168.3 C class에서 네트워크 id가 2개로 분할이 됬다.
(하지만 요즘은 넷마스크로 서브네팅을 하지 않는다.)
(요즘은 VLAN등 좋은 장비들이 나와서 그 장비를 이용한다.)

* 공인망, 사설망
- 공인망(Public Network)
   : 공인 IP를 사용하는 네트워크

- 사설망(Private Network)
   : 사설 IP를 사용하는 네트워크

 사설 IP ??
 -> 공인망과 분리된 네트워크, 공인망과 격리되었다.
 -> A class (10번)
    B class (172번)
    C class (192번)
 -> NAT  (Network Address Translation) 네트워크 주소 변환
 : 사설망도 외부와 통신이 되게하기 위해서  사설망과 외부망이 연결되는 지점에 공인IP하나를 준다.
 사설망 내에서 밖으로 나갈때 공인 IP로 바꾸어서 내보낸다.


 IP fragment Vs TCP segment  (UDP는 segment가 없다.)

* IP fragment ( IP 단편화/조각,파편... )
 - IP 패킷을 조각내는 것이다.
 - IP 패킷으로 패키징할 대 너무 크니까 적정크기로 잘라서 패키징한다.
 -> 이것이 IP fragment라고 한다.
 - 적정 크기는 MTU로 정한다.
 - MTU (Maximum Transfer Unit) (최대 전송 단위) : 보통 1500바이트




(2005바이트를 보내는 화면)



(2005바이트 수신)



위 결과를 보면 IP패킷이 분할되서 전송된것을 볼 수 있다.

ex)
우리가 2000바이트를 udp로 보내는 경우
 보낼려는 데이터 2000바이트
 
 거기에서 udp헤더가 붙는다.
 -> 2008바이트
 - 앞에 1500만큼 잘라낸다.
 
 그리고 IP패킷으로 패키징,
 -> IP패킷이 2개 나온다.
 - 도착지에서는 이걸 다시 하나로 합쳐야된다.
 - Identification을 보고 같으면 합친다. 
 분할된건지 어떻게 알까? -> Flag를 보면 안다.  
0으로 되있으면 분할되지 않았다. DF면 분할하지 마라. MF는 뒤에 더 있다라는 내용이다.

정리. (Flag비트)
 0010 0000 ->(More Fragment) MF 맨앞이 1이면 32 -> 이거면 뒤에 분할된게 더 있다는 뜻이다.
 0000 0000 -> 아무것도 분할되지 않았다는 뜻이다.
 0100 0000 -> (Dont Fragement) : 분할하지 마라

 offset은 원래 분할되기 전의 위치를 뜻한다.

- UDP는 segment가 없다. 분할된건지 확인할 헤더 필드가 없다.
- TCP단에서 segment가 된다.
TCP segmentation ( TCP 조각, 파편...)
 - TCP segment를 조각내는 것이다.
 - MSU( Maximum Segment Unit ) : 1500 (윈도우에서)
 ->그래서 일반적인 PC에서는 이미 TCP단에서 1500바이트에서 이미 조각 났기때문에
 IP fragment를 보기는 힘들다. -> IP에서는 분할되지 않아도된다. 일반적인 PC에서
 -> TCP 패킷을 의도적으로 작게 조각내서 보내면 방화벽을 통과시킬 수 있다.
 -> TCP에는 시퀀스 넘버가 있어서 재조립도 가능하다.

 * 요새는 offset을 보지않는다. 취약점때문에 사용하지 않는다.
 왜냐하면 이걸 이용해 방화벽우회도 할 수 있기 때문이다. 혹은 offset 설정으로 Tear Drop 공격기법이 있다.

상황
서버가 있다고 가정해보자.

 서버가 있다. 서버에 방화벽을 둔다. 
 방화벽 두개가 같이 있어야한다. ( host기반의 방화벽(윈도우즈방화벽), 네트워크 기반의 방화벽 )
 IP패킷의 최소 8바이트까지 쪼갤수있다. 작게 쪼개진 패킷들을 보낸다.
 방화벽은 첫번째 프레그먼트만 확인 후 이상이 없으면 나머지는 그냥 통과시킨다.
 성능면때문에 첫번째 fragment만 확인한다.
 
 그래서 offset을 조작해서 통과한 패킷들을 서버에 도착해서 조립을 하면 중첩되면서 새로운 데이터가 된다. (요즘은 IP는 안되지만 TCP에서는 된다.)
 
 그리고 뚫고 들어오면 이제 호스트기반의 방화벽이 있다.
호스트기반의 방화벽에서는 천천히 잘 조립해서 확인한다. 그래서 서버에서 막힐 수 있다.
 그래서 호스트 기반의 방화벽에서는 우회할 수 없다.

 이 방법으로 네트워크 방화벽을 우회할 수 있다.
 그러면 네트워크 기반의 방화벽은 필요없는거 아닐까?
 -> 그렇지 않다.  DDos 공격이 있다.

 * DDos 공격
 -> 대량의 패킷을 서버에 보내 서버의 부하를 많게 하여 다운되게 하는 것이 목적이다.
 -> 호스트 기반의 방화벽에서는 막지 못한다. 이미 호스트 기반의 방화벽까지 들어왔다는거 자체가 공격 성공이다.
 --> 그래서 방화벽 두개를 운영하는게 맞다.

 : 현재는 offset을 조절하는 공격은 쉽지 않다.



오늘은 어제 작성했던 패킷을 덤프하는 프로그램작성을 완성시켰다.

TCP의 데이터까지 출력할 수 있게 만들었다.



(패킷 덤프 프로그램 코드)




(실행결과.(왼쪽-패킷내용, 오른쪽-TCP 통신프로그램))



이 때 Data를 보내는데 TCP에서 많은 패킷이 왔다갔다 한 것을 볼 수 있었다.

TCP헤더의 flag 필드를 보면 패킷의 용도를 확인 할 수 있다.

* TCP는 통신하기 앞서 동기화과정이 먼저 들어간다.


(SYN 패킷)


위 캡쳐한 패킷을 보면 Flag가 SYN으로 나와있다.
캡쳐한 패킷내용을 보면 IP주소 165가 client고  239가 server여서 server로 동기화 요청하였다.



(SYN-ACK 패킷)



그 다음으로 서버측에서 SYN-ACK 패킷을 보내오는 것을 확인할 수 있다.
ACK 패킷은 응답을 주는 것이다. 또 서버측도 내게 동기화를 맞춰야해서 SYN패킷을 보낸다.



(ACK 패킷)



마지막으로 클라이언트쪽에서 서버로 응답 ACK 패킷을 보내는 것을 확인할 수 있다.
여기서 데이터도 같이 보냈는데,
지금 상황에서는 데이터보내는 겸 같이 ACK 패킷을 전송한 것이다.
바로 이런식으로 보내는 것을 Piggyback이라고 한다.

바로 위에서 본 것이 3 Hand Shaking 과정을 본것이다.
3 Hand shaking은 데이터를 주고 받기 전에세션을 맺는 과정이다.

전에 확인했던 걸 생각해보면
TCP에는 상태라는 게 있었다. (Listen, Established...)
그래서 Listen 상태일 때 SYN패킷이 오면 응답을 해주고 쓰리핸드쉐이킹과정을 할 수 있다.
Listen 상태가 아니면 응답을 못해주게 된다. (이점이용하면 SYN Flooding 공격을 할 수 있다.)

3 Hand shaking이 진행됨에 따라 서버측의 상태변화
Listen -> Syn receive -> sysn sent -> established

* 세션을 끝낼때는 4 Hand 쉐이킹 과정이 있다.
  이건 나중에 확인해 보겠다.

* Syn 패킷에 들어있는 시퀀스 넘버를 ISN (Initial Sequence Number) 초기 시퀀스번호라고 한다.
-> 서버에게 나를 식별할 수 있는 번호를 보내준다. 서버와 클라이언트는 이 시퀀스 번호를 서로를 알아본다.
-> SYN 패킷의 목적은 이 ISN을 서버에 전달하는 목적이다.

3 Hand shaking 동작 내용.
1. 클라이언트는 SYN 패킷에 ISN을 넣어서 보낸다.
2. 서버도 SYN-ACK 패킷으로 응답을 한다. 이 때 서버의 ISN을 생성해서 클라이언트에 보내면서 응답을 한다.
(서버의 ACK 필드에는 클라이언트의 시퀀스넘버에 1을 더한 값이 들어간다.)
그래서 이 시퀀스 넘버가 틀어지지 않는 이상 이 연결은 유효하다.
3. 클라이언트는 ACK 패킷을 전송한다.


* 세션 하이재킹 -> 남이 로그인한 세션이 인증된 패킷 시퀀스 넘버를 예측해서 그 값을 데이터로 만들어서 보내면 서버에 로그인할수있다.

-> 시퀀스 넘버는 서로 주고받는 크기만큼 늘어난다.
* ACK -> 잘 받았다는 응답이다.
만약 이 패킷을 못받으면 잘 안간줄 간주하고 다시 보낸다.
이게 TCP가 가지고있는 에러정정(Error Control)이다.

이걸 응용하여 다음 ACK응답으로 올 번호를 예상하여 출력해보았다.



(추가한 코드)


(예상 다음 ACK 번호와 일치)



확인해보면 다음 받을 ACK 번호랑 예측한 값과 같음을 확인할 수 있다.

* IP에 관해 이야기를 시작하겠다.

Layer 3
 - IP - 경로 설정
       - 네트워크 상에서 호스트를 식별하는 식별자(IP주소)로도 사용한다.
       - 라우팅 프로토콜이 들어간다.
 -> ( 라우팅 프로토콜을 이용하여 취약점을 공략해 공격할 수 있다.)

 PDU : Packet (IP 패킷)
 - 라우팅 장비가 IP주소를 보고 어디로 보낼지 결정한다.

IP address
 -> IANA에서 IP주소 관리, 할당, 표준 제정등을 한다.
     - IPv4, IPv6

- 주소의 분류 (체계적으로 관리하기 위해서)
: IPv4의 주소 체계이다.
 - 4자리 자연수를 '.' 으로 구분해서 표기한다.
 - 1자리 수는 0~255까지 표현이 가능하다.
    전체 사용가능한 ip주소 범위 -> 0.0.0.0 ~ 255.255.255.255
   -> 실제 사용 불가능한 주소가 있다.
   ex)  0.0.0.0    ( 모든 ip를 의미한다. ) 
        255.255.255.255  (브로트캐스팅 주소)
: A class, B class, C class, D class, E class

IP 주소 클래스

A Class
 -> 앞에 한자리가 구역을 나타낸다. 이것을 network id라고 표현한다.
 -> 나머지 세자리는 host id이다.
 -> 네트워크 id : 첫번째 자리
 -> host id : 나머지 세 자리. (host를 구별하기위해 사용)
 -> 맨 앞 1비트가 0으로 공통이다.
ex) 1.xxx.xxx.xxx
    10.xxx.xxx.xxx
-> 2진수로 표현하면 0000 0000.hhhh hhhh.hhhh hhhh.hhhh hhhh
-> 맨 앞비트가 0이어야한다. 즉, 0000 0000 ~ 0111 1111 까지 표현 가능하다.
 -> 10진수로 따지면 1.h.h.h(10) ~ 126.h.h.h 까지 가능한 A클래스 주소 범위다. (0도 안된다.) (127도 가상ip주소이므로 사용못한다.)
 -> 또 10.h.h.h 도 사용하지 못한다. -> 사설 IP주소이다.
 -> 127로 시작하는건 loop back 주소로 자기자신을 표현하는 IP주소이다.
 ex) 1.0.0.0 ~ 1.255.255.255 ( 1.h.h.h 주소를 가지고 있다면, 이만큼 쓸수 있는 것이다.)
  -> 1.0.0.0 (x)
  -> 1.255.255.255  (x)  
  -> 이 두개는 사용하지 못한다. 이유는 1.0.0.0 은 1.h.h.h를 대표하는 IP주소이고
      1.255.255.255는 1.h.h.h 네트워크에 있는 전체에 보내는 브로드캐스트주소이다.
      실제 사용 가능한 주소는 1.0.0.1 ~ 1.255.255.254 이다.






저번주까지 UDP 패킷분석, 그리고 IP 패킷을 분석하였다.

오늘은 TCP 패킷을 분석해보았다.

TCP는 UDP와 달리 조금 까다로운 부분이 많았다.

포함관계를 다시 정리하자면

IP 패킷안에 TCP 혹은 UDP가 들어가는 것이다.

저번주 까지 했던 패킷을 덤프받는 프로그램에서
이제 IP헤더를 분석할 수 있었고
그에 따라
소스 IP주소와 목적지 IP주소를 파악할 수 있었으므로

조금 더 정교한 필터링을 할 수 있게 되었다.



(조건 문에 IP주소로 필터링 하는 코드)


또, IP 헤더에서 Protocol 타입을 알아낼 수 있으므로
TCP, UDP, ICMP를 구분하기로 했다.



(Protocol 필드로 구분하는 모습)


위 코드를 보면 이제 TCP 헤더를 분석해볼 것이므로
처음에 list를 이용해 16진수로 표현해보았다.




(오른쪽화면은 저번주에 만들었던 TCP 메세지전송 프로그램.)


위 화면을 보면 TCP 데이터가 나오는 것을 확인 할 수 있다.

UDP와 다른점은 UDP는 메세지 하나 보내면 끝이었는데
TCP는 그 전에 많은 패킷들이 왔다 갔다 한다.
자세한 특징은 다음에 더 알아보겠다.
우선,
TCP 패킷의 헤더를 분석하는 것이 먼저다.

우리가 덤프받은 TCP 패킷의 헤더
['0xc3', '0x29', '0xea', '0x60', '0x2a', '0x4', '0x15',
'0x1a', '0x0', '0x0', '0x0', '0x0', '0x80', '0x2', '0x20', '0x0',
 '0xd9', '0x88', '0x0', '0x0', '0x2', '0x4', '0x5', '0xb4', '0x1', '0x3',
'0x3', '0x2', '0x1', '0x1', '0x4', '0x2']

내용은 이렇다.
1. 출발지 포트번호(2바이트) : '0xc3', '0x29' = 0xc329 = 49961
2. 도착지 포트번호(2바이트) : '0xea', '0x60' = 0xea60 = 60000
3.  Sequence Number(4바이트) : '0x2a', '0x4', '0x15', '0x1a'
- 운영체제에 의하여 랜덤하게 시작된다. Session을 표현하는 고유값이다.
4. Acknowledge Number(4바이트) : '0x0', '0x0', '0x0', '0x0'
*'0x80' -> 8과 0으로 본다.
5. TCP 헤더의 길이 (4비트) -> 8 (x4) = 32바이트
6.  예약된 영역 : 0 (사용하지 않는 필드이다.)
7. Flag : '0x2' = 2 (여기서는 SYN패킷, 동기화요청)
Flag 종류
1 : FIN
2: SYN
4: RST(reset)
8: PSH(push)
16: ACK(응답)
32: URG(urgent) 
(만약 동시에 설정된다면, 예를들어 SYN과 ACK가 같이 설정되면 두개의 합이된다.)
(ex. SYN-ACK 은 18번이 된다.)

8. window size (2바이트) : '0x20', '0x0'
9. TCP의 Checksum(2바이트) : '0xd9', '0x88'
10. Urgent Pointer(2바이트) : '0x0', '0x0'
---- 여기까지 딱 20바이트다.-----------------

그 이후로 옵션이 더 붙어서 패킷이 보내질 수도 있다.

자. 그러면 이제 이 내용을 바탕으로 헤더내용을
분석하여 출력해보겠다.


(TCP 패킷 분석코드)


(TCP 헤더 분석 내용)



여기서
Header 길이를 표현하려면 아까 한 바이트를 4비트로 나누어야한다.
이제 그 작업을 시작하겠다.

0x45를 4와 5로 분리하는 것이다.
먼저 0x45를 2진수로 변환한다. (bin()함수 사용)

그 후 슬라이스로 4개씩 자르고 int로 형변환 해준다.
아래와 같다.


(분리된 모습)



이걸 이용해
IP 헤더의 버젼과 헤더길이를 나누고
TCP의 길이를 구하겠다.

또 IP헤더에서 헤더길이를 정확하게 구할 수 있고 그러면
UDP,TCP 패킷이 어디서부터 시작하는지 또한 정확하게 표현할 수 있다.

(지금까지는 그냥 IP헤더가 20바이트라고 가정하고 했었다.)




(헤더 길이를 분리하는 코드)



(UDP의 시작 슬라이스와 TCP 헤더 길이, 또 flag를 표현해 수정해주었다.)



flag를 dictionary로 정의하여 키값을 대입하면 그에 대응하는 값이 출력된다.



(결과 화면)



지금 까지 TCP패킷을 분석해보았고

IP헤더도 더 정밀하게 분석하였다.

내일은 현재 진행중인 패킷 분석 프로그램을 조금 더 수정하고
본격적으로 패킷을 확인하고 변조해 보겠다.









어제
UDP를 이용해 데이터를 주고받는 프로그램을 만들었고

그 데이터를 덤프시킬 프로그램도 만들었었다.

이제 그 데이터를 분석해보겠다.


(어제 했던 Hello 데이터를 전달하는 UDP 패킷내용)


Hello 앞에 나오는 헤더부분을 살펴보자.
b'E\x00\x00!\x11\xf6\x00\x00\x80\x11\x9f\xf1\xc0\xa8\x03\xa5\xc0\xa8\x03\xef\xce\x89\xea`\x00\r\x9a2
(이건 오늘 실행시켜서 복사한 패킷헤더이다.)

\ (역슬래쉬)
\x 로 표시는 16진수 표시이다.

왜 어떤건 문자로 나오고 16진수로 나올까?
-> 문자로 나올수 있는 것은 문자로 나온다.
16진수 중에 문자로 표시할수 있는건 문자로 표시한다. (이것도 16진수)
(아스키 코드에 따라)

앞에 b라고 붙어있다. -> 실제 네트워크 스트림에 사용하는 데이터가 파이썬에서 맞지 않는다.
       -> 그래서 C에서 API를 가져온다.
       -> C언어가 쓰는 바이트타입을 가져온것이다.
       -> 그래서 다시 파이썬에 맞게 바꾸는 함수가 있는데,
           그건 - pack()
                  - unpack()
             함수이다.

이 함수들은 struct 모듈 안에 들어있다.


(unpack의 사용 모습 (파이썬공식홈페이지 출처))


hhl 표시  -> 각각 2바이트 2바이트 4바이트로 묶어서 출력하라는 뜻이다.

우리는 앞에부터 20바이트는 뺄것이다. 이 패킷은 IP헤더이기 때문에
20바이트 뒤부터 분석을 해볼 것이다.


(slice로 앞 20바이트는 잘라내는 모습)



(IP헤더는 떨어지고 UDP 패킷내용만 남은 모습)



여기서 unpack을 사용하지않고
list를 사용해 전부 hex 표시로 바꾸겠다.

* map() 함수 - 두번째 인자를 첫번째인자(함수)에모두 입력한다.
hex 또한 함수이기 때문에 지금 쓰는것은 두번째 인자를 모두 16진수의 수로 바꿔주는 것이다.

(hex 표시로 바꿔주는 코드)



(16진수로 표시되는 모습)



전부 16진수로 나오는 모습이다.

뒤에 다섯자리 '0x48', '0x65', '0x6c', '0x6c', '0x6f' 를 아스키코드에 대입하면
Hello 라고 우리가 보내는 데이터임을 알 수 있다.

그리고 데이터 앞자리 우리가 구한 UDP헤더의 내용이다.
['0xcc', '0xe8', '0xea', '0x60', '0x0', '0xd', '0x9b', '0xd3']

헤더에는 총 4개의 내용이 들어있다. (첫번째 부터)

1. Sorce Port Number (출발지의 포트번호) 
     ->2바이트 (출발지 포트)

2. Destination Port Number (출발지의 포트번호) 
     -> 2바이트 (도착지 포트)  '0xea', '0x60'  = ea60  = 60000
3. Length (길이) 
     -> 2바이트  = '0x0', '0xd  = 0d   =13  = 13바이트 
(헤더 8바이트, UDP데이터 5바이트) 13바이트가 맞다.

4. Checksum
     ->2바이트   검사용

-> 이런게 Protocol 프로토콜이다.
처음에는 데이터 Hello가 보내지는걸  봤는데 이제 Hello가 패키징 되면서 붙는 내용들을 확인한다.
그 과정중 하나가 UDP를 본것이다.

UDP 헤더의 내용은 4개 밖에 없다. -> 에러정정, Flow Control 등 기능은 없다.

* 바이트
1 바이트로 숫자 하나를 표현할 수 있다. 16진수 2자리를 표현할수 있는 것이 1 바이트 (0x00 ~ 0xFF)

이제 unpack을 이용해 좀더 정확하게 데이터를 출력해보겠다.

H -> 2바이트를 묶어서 하나로 만들어준다.
! -> network stream 데이터를 읽어들일때는 !를 붙여준다.
 붙이는 이유 네트워크에서 돌아다니는 데이터랑 컴퓨터에서 다루는 데이터랑 다르다.
 그래서 ! 느낌표를 붙여서 네트워크 데이터라라는것을 알려줘야한다.

이제는 조금 구분 할 수 있게 됬다.


(UDP 헤더 패킷 분석 코드)


-> 목적지 주소가 60000 포트인 경우에만 출력한다. (내가 60000번 포트로 패킷을 전송할거기 때문에
       내가 보내는 패킷의 정보가 궁금한 것.)


(UDP 헤더 분석)



지금까지 Hello라는 데이터를 UDP로 보낼때의 패킷 내용들을 분석해 보았다.

윈도우 소켓으로 볼수있는 것은 L3 까지 볼 수 있다.

- Layer 4 ( Transport Layer ) 의 프로토콜(UDP, TCP)
- L4에서는 데이터가 어디서 어떻게 전달되는지 해당 계층에서는 상관이 없다.
- 에러 정정(UDP에는 없다.), 흐름제어(UDP에는 없다.), 이런 내용만 포함하고 있다.

- L4에서는 관련된 정보만을 포함한다.
-> UDP의 checksum에서는 오류가 났는지 안났는지만 판별 오류났으면 버린다. 그 외의 행동은 하지 않는다. 그래서 에러정정의 기능이 없다고 말한다.


* UDP인지, TCP인지 어떻게 판별할까?
: 더 하위계층에서 그 내용을 가지고 있다. -> IP 패킷

우리가 잘라냈던 IP헤더를 보면 UDP인지 TCP인지 확인할 수 있다.
socket 생성시 PROTOCOL 부분을  IPPROTO_UDP 를 IPPROTO_IP로 바꾸었다.



(socket 타입 변경)


UDP = User Datagram Protocol

IP 헤더 ( Internet Protocol Header )
-> L3 ( Network Layer (네트워크 계층) )
-> IP는 해당 계층의 대표적인 프로토콜이다.
-> 데이터를 송신지에서 수신지로 향하게 하는 데이터의 전달과 밀접한 관련이 있다.
-> 주요목적은 전송경로 설정 등이 있다.
-> 경로에대한 담당은 라우터가 하는것이다. 즉 IP헤더에는 IP주소만 적혀있고 라우터가 IP주소를 보고 경로를 설정한다.
-> 사이즈는 기본 20바이트이다. (추가로 옵션이 있긴하다.)
현재 우리가 보는 것은 IPv4의 IP헤더 이다. IPv6는 또 조금 다르다.

Layer2, Layer1의 내용들은 현재 윈도우즈 환경에서는 지원하지 않는다.
윈도우즈 소켓은 Layer3 까지 밖에 지원이 안된다.

이제 IP헤더를 분석해 본다.

(list를 이용해 출력하는 코드)



(IP 헤더 출력 모습)



출력한 IP 헤더
['0x45', '0x0', '0x0', '0x21', '0x35', '0xd3', '0x0', '0x0',
'0x80', '0x11', '0x7c', '0x14', '0xc0', '0xa8', '0x3', '0xa5',
'0xc0', '0xa8', '0x3', '0xef']

ip 헤더 분석
총 20바이트로 되어있다. 

첫번째 바이트 숫자 하나가 2개의 값을 의미한다.
   0x45  -> 4랑 5로 읽을 수 있다.
1. Version -> 맨 앞 4 = IPv4
2. Header length -> 그 뒤 숫자 5 = 헤더의 길이 : 4를 곱해준다. 5x4 = 20 바이트  헤더의 길이다.
3. Service  -> 현대 IPv4에서는 사용하지 않는다. 거의 항상 0으로 초기화된 값이 들어간다.  : 0x0'
4. Total length (전체길이) : 그다음 숫자 2바이트 '0x0', '0x21' = 0x021  = 33바이트
    ( Ip헤더 20바이트, udp패킷 13바이트 )
5. Identification (식별자) : 그다음 숫자 2바이트 '0x35', '0xd3'
6. Flag ->  '0x0'
7. Fragment OFFSET ->  '0x0'
* IP패킷은 여러개로 분할 될 수도있다. 그래서 5,6,7 정보가 필요하다.
    자세한 내용은 다음에 확인하겠다.
8. TTL (Time to live) -> '0x80'  = 128.  ( 윈도우즈에서 만들어진 패킷은 기본적으로 128로 나간다.)
 TTL은 네트워크를 방황하는 패킷들을 예방하기 위해서 있다.
 TTL은 라우터를 한 Hop이라고 지날수 있는 Hop의 수이다.
9. Protocol 타입 -> '0x11' = 17 :  (udp :17번) (tcp:6번) (ICMP: 1번)
10. Header checksum -> '0x7c', '0x14'  checksum은 각 헤더마다 들어있다.
11. 출발지 IP (4글자) : '0xc0', '0xa8', '0x3', '0xa5'  -> 192  168  3  165  
12. 도착지 IP (4글자) : '0xc0', '0xa8', '0x3', '0xef


-> 전송에 관련된 내용들을 가지고 있다.
1,1,2,2,1,1,1,1,2,4,4 개씩 unpack하면 된다. 위 데이터를 끊는 개수이다.



(IP 헤더 내용을 쪼개어 출력하는 코드)



(IP 헤더 분석)


IP 헤더에 무슨 내용들이 있는지
어떤 역할을 하는지
확인하였다.

또, UDP 패킷도 함께 분석하였다.

-> 나중에 이걸 이용해 패킷 스니퍼를 만들것이다.
(비슷한 원리로 만들것이므로 기억해둬야한다.)













어제 하던 통신프로그램을 보완해서

그럴싸한 통신을 하는 프로그램으로 업그레이드 했다.



(while 문을 이용해 메세지를 종료할 때 까지 통신하는 코드)


(메세지를 주고받는 모습)


(맨 아래줄에 보면 외부 IP주소와 포트번호가 나와있다.)



위 화면에서 외부주소 맨 아래를 보면 상대 IP주소와 포트번호가 나와있다.

코드에 적힌 대로 IP주소와 포트번호로 연결된것을 확인할 수 있다.

현재, 이 프로그램은 동기식 통신이다. 즉 한번 보내고 한번 받고 해야하는 방식이다.

여기서,

상대와 보통 채팅처럼 비동기식으로도 만들 수 있다.
내가 보내지않아도 상대가 마음껏 보낼 수 있는 상태로.

그러려면
Thread를 이용해야한다.

하나는 send만 하고 하나는 recv만 하도록 쓰레드를 열어서 실행해야한다.

하지만 현재 우리는 네트워크를 이해하는 차원에서
여기 까지 하겠다.

우리가 패킷을 분석하기 위해 만드는 프로그램이기 때문이다.

우리가 위에서 보낸 메세지나
네트워크에서 주고받는 데이터는
전달하기위해 여러가지가 붙어서 패킷을 만들어 전달된다.

이런 규칙들이 프로토콜인데
4계층의 TCP, UDP를 알아보겠다.

먼저 파이썬 코드로 어떻게 동작되는지 두 프로토콜의 차이점을 봐야한다.

동작 과정

* TCP server
 1. socket()
 2. bind()
 3. listen()
 4. accept()
 5., send(), recv()

* TCP Client
 1. socket()
 2. connect()    -> server의 accept와 연결된다.
 3. send(), recv()

* UDP server
 1. socket()
 2. bind()
 3. sendto() ,recvfrom()

* UDP server
 1. socket()
 2. sendto(), recvfrom()
-> send()를 못쓰는 이유는 send는 연결되어있는 곳으로 보내는건데
UDP는 연결된게 없으므로 sendto()로 그때그때 어디로 보내는지 적어줘야한다.

UDP의 특징
-> 연결과정 없이 바로 보내고 받는다.
-> 데이터가 갈수도있고 안갈 수도 있다. 그래서 TCP는 신회성이 있는 통신이다. 왜냐하면 안갔는지 갔는지 체크하기 때문이다. TCP는 체크하지만 UDP는 체크하지 않는다.
-> UDP는 신뢰할 수 없는 통신이다. ( 이말은 상대가 받았는지 체크할 수 없다는 뜻)
-> 인증 절차도 없다. 그냥 보내고 그냥 받는다.
보통 UDP를 사용하는 프로그램은 보통 인증과정이 없다.
UDP자체로는 인증을 할 수 없다.

그렇다면 왜 쓸까?
  위 말만 들으면 왜쓰는 걸까 싶지만 UDP의 큰 장점은 복잡하지않고 빨리빨리 보낼 수 있다는 것이다. 보내는 데이터가 크지 않다면 UDP가 적절하다. UDP는 에러체크등이 없고 그냥 보내기만 하는 것이다. 그래서 사실 예전에는 많이 쓰이지 않았다. 하지만 요즘 많이 쓰이고 있는것이 실시간 통신이 필요할 때 특히 UDP를 많이 쓴다. 빨리보낼 수 있고 에러 났다고 다시 보낼 필요가 없는 실시간 통신에 적합하다. 혹은 짧은 데이터 같은 경우가 적합하다.

- netstat -anp UDP
입력하면 UDP 포트 정보를 볼 수 있다. 여기에 상태 정보가 없다. 연결과정이 없기 때문이다.


(상태 부분이 없다.)


파이썬에서 UDP에 사용되는
recvfrom() 함수.
-> 받아온 내용보면 클라이언트 정보도 같이 출력된다.


(맨 아래 데이터와 데이터가 날라온 IP주소와 포트정보)


-> 튜블정보이므로 ip주소와 포트번호를 따로 입력받아서 출력해 봤다.


(튜플정보를 따로 받아와 출력한 모습)


TCP, UDP  -> Layer 4 (4계층)에 속한다.
-> 전송계층 (Transport Layer)

* PDU : Protocol Data Unit


-> packet : 패킷, 3계층에서 사용되는 단위
    근데 그냥 보내는 데이터 덩어리를 패킷이라고도 한다.


L4에서는 pdu가 segment 다.

-> 주소 체계 : Port번호

-> 각 계층별 사용하는 단위와 주소가 다르다.


패키징
-> Hello 데이터를 패키지화한다. 포장하듯이.
-> UDP 헤더를 붙인다. 데이터 앞에

헤더에 어떤 정보들이 들어가있는지 정해논다 -> 규약 Protocol

프로토콜에 맞게 데이터를 패키징해서 다음 계층으로 보낸다.



UDP 헤더에는 어떤 정보가 들어있을가?


UDP Header

 - dst Port 번호, src Port 번호
 - data length, checksum



-> 직접 확인해본다.

(먼저 패킷 데이터를 덤프받을 프로그램을 만들었다.)


socket.gethostname() 을 이용하면 컴퓨터의 이름을 얻어올 수 있다.

gethostbyname() 을 이용하면 이름을 가지고 호스트 정보를 가져올 수 있다. (IP정보)


-> 여기서 포트번호에 0을 쓴이유는 특정포트를 보기위해 하는게 아니라 특정 IP에 모든 포트들을 확인하는 것이다.


(여러번 실행한 경우)


뭔지 모를 데이터들이 오고가는 모습이다.


그러면 우리가 오늘 바로 작성한 UDP로 패킷을 보내고 이 패킷의 내용을 확인해보자.


(코드)


위 코드를 보면 info[0]에는 IP정보가 들어가므로 IP주소가 내꺼인 주소만 보겠다는 것이다.

이걸 안하면 이것저것 위에서 본거 처럼 구별하기가 힘들어서 그렇다.


이제 내가 UDP로 패킷을 보내고 이 프로그램으로 확인 해 볼 것이다.

보낼 데이터 : Hello


(출력 화면)


그림은 클릭하면 크게 보인다.


출력화면을 보면 끝에 Hello라고 적혀있는것이 보인다.

그리고 그 앞부분은 헤더의 내용이다.

UDP는 헤더에 많은 내용이 들어간다고 안햇는데 왜이렇게 길지???..


그건 UDP헤더만 붙는게 아니기 때문이다.


아래 계층으로 내려가면서 패키징되는데 점점 헤더가 붙어서 늘어나기 때문이다.


이제 앞으로 이 헤더 정보, 데이터 정보를 구별하여 읽는 법을 공부할 것이다.


다른 패킷을 분석하여 어떤 정보가 오고가는지 확인할 수 있게 될 것이다.







네트워크란


- 떨어져 있는 Host들간의 연결 혹은 연결되어있는 집단을 말한다.


네트워크의 기원


ALPANET -> 정부, 학교, 기업들간에 서로 데이터공유를 원활하게 하고자 해서 시작된 프로젝트이다.
              ->  이 때, router등 네트워크 장비들이 개발되었다.


그렇다면
네트워크에서 어떻게 데이터를 주고받을까?


-> 여러가지 프로토콜(규칙들)을 통해서 주고받는다.


네트워크에서 통신하는 모습을 오늘 확인해 볼 것이다.


네트워크의 구조부터 봐야한다.


* 네트워크 구조는 OSI 7 Layer (표준)로 이루어져있다.


크게 보면 아래와 같다.


1~4 계층
 - 하위 4계층이라고 한다.
 - 물리적인 전송 방법에 관해 역할을 담당한다.
 - 통신규약(프로토콜)
     -> TCP, UDP, IP, ARP, ETHERNET, ...  (데이터를 전달하는 방법들)


5~7 계층
 - 상위 3계층이라고 한다.
 - 논리적인 규칙에 따라 전송된다.
 - HTTP, FTP, Mail,... (프로토콜)
 - kakao talk, game.. (비표준프로토콜) -> 외부에 공개되지 않는 규약들

먼저, 하위 4계층을 이용해 문자를 전송해보자.
파이썬을 이용해 1~4계층으로 메세지를 직접 주고받는 통신을 해볼 것이다.
파이썬에서 통신을 하기 위해 필요한 내용들이다.


* 파이썬 소켓 프로그래밍

-> 우리가 하려는 것이 네트워크 상에서 데이터를 주고받는 모습을 확인하려는 것이다.
    그러기 위해서는 네트워크 프로그래밍을 해야한다. 네트워크 프로그래밍을 소켓 프로그래밍이라고도한다.
-> 네트워크 프로그래밍? : 통신을 위한 프로그래밍으로 소켓을 이용한다. 그래서 소켓프로그래밍이라고 하는 것이다.

 - socket? 소켓을 이해하기 위해 다음것들을 먼저 보는 것이 도움이 된다. 


* 입/출력 방식

 - 표준 입/출력 : input(), print()   (키보드, 모니터) (키보드, 모니터로 이루어지기 때문에 다른 식별자가 없다.)
 
- 파일 입/출력 : 파일 식별자가 필요하다. (어떤 파일을 열었는지 알아야하기 때문에 식별자가 필요하다.)

  - open ( 파일을 가져온다 )
   a, w, r    (추가, 쓰기 읽기전용)
   a -> 있으면 거기다 쓰고 없으면 새로 만든다.



(open() 을 이용해 파일입력해보는 코드)


(이렇게 바탕화면에 text 파일이 생긴다.)



핸들러 -> 파일 변수. 파일을 다룰수있는 변수


(핸들러 내용)


open 되있으면 파일이 열려있는것이다. 이 때 삭제를 누르면 열려있다고 삭제가 안된다. (열려있다는거 확인할 수 있다.)
close로 닫을 수 있다.


(Python 에서 close()를 하지 않은 경우)


(close() 를 해야 열려있던 파일이 종료된다.)


write -> 화면에는 입력한 문자의 길이가 표시된다.

* 파일을 다룰 때는 식별자를 주의해야한다.


(파일에 문자를 입력해본 결과(11이라고 적힌건 입력된 문자수를 출력한다.)


파일 접근 방법
ex) fd = open("파일 경로", "모드")

네트워크 입/출력도 파일 입/출력과 비슷하다고 생각하면 된다.
파일은 open으로 접근했다면 네트워크는 socket으로 접근한다고 생각하면 된다.

 - 네트워크 입/출력 : socket (파일의 디스크립터와 같은 맥락) 소켓은 식별자이다.
  ex) 서버는 여러개의 소켓꽂을 자리를 준비하고있다. 우리는 소켓을 들고가서 콕 꼽는다.
 -> 네트워크 프로그래밍을 소켓프로그래밍이라고도 한다.
 먼저 소켓을 생성해줘야한다. (파일입출력할때 파일을 생성한것처럼.)

1. socket 생성
 - socket module
 - 소켓 인자 : (1. 인터넷 타입, 2 통신 타입,  3. 프로토콜)

*인터넷 타입
 - AF_INET  (IPv4)
 - AF_INET6
 ...
 - AF_PACKET

* 통신 타입
 - 통신하는 타입
 - SOCKET_STREAM  (TCP)
 - SOCK_DGRAM     (UDP)
 - SOCK_RAW

* 프로토콜 -> defalt 값은 0이다. (안적어도 된다.)

출력해보면 볼수 있는데 fd가 중요하다. 소켓마다 번호가 다르다.
하나 더 만들면 번호가 다르게 생성된다.


(소켓 생성 모습)


* 네트워크 타입

 1. 서버-클라이언트 ( 1: N )
 2. Peer To Peer ( 1 : 1 )  (카톡, 채팅)
    -> 알고보면 이것도 서버-클라이언트다
    -> 누군가는 서버가 되야하고 클라이언트되야한다.
     -> 누군가는 반드시 전화를 먼저 걸어야한다. (전화를 예로)

윈도우즈 위에 여러개의 프로그램이 있다. 그래서 포트라는 개념이 필요하다.
Port 번호를 가지고 프로그램을 식별한다.
포트 번호는 ( 1~ 65535번 ) 까지 사용한다.
1~1024번 포트는 well-known 포트번호라고 한다. 조금 공식적인 포트 번호
1~10000번 까지는 비우는게 좋다. (요새는 통신하는게 많아져서)

* IPv4, IPv6 
 - IP Address : 네트워크상에서 호스트를 식별할 수 있는 식별자(식별번호)

ipv4
 - 4자리의 정수를 '.' 으로 구분해서 표시한다.
 - 각 정수의 범위는 (0~255) 이다.

ipv6
 - 6자리의 16진수 정수를 가지고 주소를 표현한다.
 - 0x00 ~ 0xff (255개)

커맨드창에 아래와 같이 입력하면
netstat -anp tcp
-> 현재 열려있는 포트들을 보여준다.



(netstat -anp tcp 명령어)


(포트 번호 50000번을 열어둔 상태)



(포트번호 50000번이 열려있는 것을 확인할 수 있다.)


이제 간단한 통신을 하는 프로그램을 만들어 볼것이다.

동작 방식은

* 서버 프로그램의 동작방식
1. 소켓을 생성한다.  -> socket()
2. 포트를 오픈한다.  ->bind()   : 튜플 형태로 하나의 인자. 튜플안에 뒤는 포트번호
3. 네트워크의 상태를 Listen 상태로 변경한다.  -> listen()  괄호 안에 연결 개수를 적기도한다.
4. 클라이언트의 연결 요청을 기다린다.     -> accept()  : 클라이언트 소켓을 리턴해준다.

* 클라이언트 프로그램의 동작방식
1. 소켓을 생성한다.   -> socket()
2. 서버에 연결한다.   -> connect()  : IP주소와 포트번호를 튜플형태로 적어준다.



(통신 프로그램 코드, connect()는 빠져있다.)


(서버 측 출력)


위 코드를 보면 서버 모드일 때 연결이 되면 연결되어있는 정보가 출력되는 것을 확인 할 수 있다.
리턴되는 값은 클라이언트 소켓 정보이다.

연결된 이후에는 send, recv 함수를 통해서 데이터를 송/수신 할 수 있다.

그러면 이제 메세지를 주고받겠다.



(문자열 abcde를 보내는 클라이언트 모드)



(에러가 났다.)

문자열을 그대로 보내면 파이썬은 처리를 못한다.
그래서 .encode()를 하면 파이썬이 적절하게 인코딩을 해서 보내주게 된다.


(서버에 입력이 들어온 모습)


내가 클라이언트 모드일때 abcde를 보냈고
내가 서버모드로 하고 옆사람이 클라이언트모드로 내게 Hello를 보낸 것을 확인 할 수있다.

출력 결과를보면 첫번째 꺼는
b' Hello라고 적혀있고
아래꺼는
Hello 라고 적혀있다.

첫번째꺼는 데이터 받은 것을 디코딩하지 않고 바로 출력한것이다.
보면 바이트 표시가 되있는 것을 볼 수 있다.

두번째 꺼는 .decode()를 한 모습이다.










+ Recent posts