요청 메시지인 경우 Type = 8

응답 메시지인 경우 Type = 0

 

 

ICMP 메시지 구성

ping 요청 패킷과 응답 패킷의 시간을 보고 패킷 전송에 소요된 시간을 출력한다.

40번째 줄에서 identifier에 들어갈 ID를 생성하고 있다.

 

 

58번째 줄에서 시그널 알람을 설정

65번째 줄에서 sig_alrm() 실행

sig_alrm() 함수

메시지 센드

121-127 ICMP 패킷 구성 

127 - gettimeofday() 현재 시각을 icmp_date에 할당하고 있다.

 

133에서 원격지로 sendto()로 보내고 있다.

 

107에서 1초마다 시각을 실어서 ICMP 메시지를 계속 보내고 있다.

다시 핑 프로그램으로 돌아와서 

69 - recvfrom()으로 메시지를 받는다.

77 - IP 헤더의 프로토콜 타입이 ICMP인지 확인한다.

80 - 자신이 보낸 메시지인지 확인한다. 

84 - 에코 응답인지 확인한다.

85 - identifier가 내가 보낸 것과 같은지 확인한다.

 

88 - 현재의 시각

89 - ICMP에 들어있는 시각

90 - 88번과 89번에서 잰 시각의 차이를 계산해서 94번에서 출력한다.

 

<ICMP(Internet Control Message Protocol)>

- ICMP는 TCP/IP에서 IP 패킷을 처리할 때 발생되는 문제를 알려주는 프로토콜이다.

- 우리 컴퓨터인 A에서 B컴퓨터의 상태를 보기위해 ping 을 찍으면 ICMP 프로토콜을 보내게 됩니다. 그렇게 되면 A 컴퓨터는 B의 네트워크 상태를 확인할 수 있습니다. 

 

<IGMP(Internet Group Management Protocol)>

멀티캐스팅 데이터의 수신을 위해서 IGMP프로토콜을 사용한다. IGMP는 인터넷 그룹 관리 규약이라고 불린다.

 

아래 사이트에서 정보를 입수 블로그를 확인해보니 네트워크 기초지식을 보기좋게 정리해두었다. 공부용으로 좋은 것 같다.

https://m.blog.naver.com/PostList.nhn?blogId=rbdi3222&categoryNo=14&logCode=0&categoryName=%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC

 

아는만큼 보인다 : 네이버 블로그

개인 복습용 자료들입니다.

blog.naver.com

 

 

https://www.youtube.com/watch?v=tVKp69n5YKk&list=PL7mmuO705dG265DxvC-oZgKViaRGt2-eS&index=34&ab_channel=%ED%94%84%EB%A6%AC%EB%A0%89

 

TCP 패킷 - 4계층에서 사용되며 포트번호를 통해 데이터 전송

 

control bit에 값중 SYN 값이 1이고 ACK 값이 0이면 src에서 dst로 보내는 값을 나타내고

SYN 값과 ACK 값이 둘다 1이면 dst 에서 src로 SYN_ACK 패킷임을 나타낸다.

 

TCP 헤더의 체크섬은 Presudo(슈도) header와 TCP header 정보를 통해 만들어지게 된다.

 

<Raw 소켓으로 패킷 수신>

22~23 번째 줄을 보면 tcp 헤더와 IP 헤더를 설정하고 있는 것을 볼 수 있다.

row 소켓 모드에서 tcp 프로토콜을 사용하겠다는 것을30번째 줄을 통해서 확인할 수 있다.

39번째 줄에있는 recvfrom()메소드를 통해서 데이터를 읽어 드린다.

44~45번째 줄을 보면 tcp 헤더와 IP 헤더를 출력한다.   

 

 

// read_packet.c
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <netinet/in.h>
#include <netdb.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <sys/socket.h>

void print_ip(struct iphdr *);
void print_tcp(struct tcphdr *);

main( )
{
	int	sd;
	int	len;

	int	rx_packet_size = sizeof(struct iphdr) + sizeof(struct tcphdr);
	char *rx_packet = malloc(rx_packet_size);

	struct tcphdr  *rx_tcph;
	struct iphdr   *rx_iph;

	struct in_addr s_addr, d_addr;
	struct sockaddr_in local, remote;

	struct servent *serv;

	if((sd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
		printf("socket open error\n");
		exit(-1);
	}

	while(1) {
		bzero(rx_packet, rx_packet_size);

		len = sizeof(local);
		if(recvfrom(sd,rx_packet,rx_packet_size,0x0,(struct sockaddr *)&local, &len)<0) {
			printf("recvfrom error\n");
			exit(-2);
		}

		rx_iph = (struct iphdr *)(rx_packet);
		rx_tcph = (struct tcphdr *)(rx_packet + rx_iph->ihl * 4);

		print_ip(rx_iph);
		print_tcp(rx_tcph);
	}
	
	close(sd);
}

void
print_ip(struct iphdr *iph)
{
	printf("[IP  HEADER] VER: %1u HL : %2u Protocol : %3u ", iph->version, iph->ihl, iph->protocol);
	printf("SRC  IP: %15s ", inet_ntoa(*(struct in_addr *)&iph->saddr));
	printf("DEST IP: %15s \n", inet_ntoa(*(struct in_addr *)&iph->daddr));

}

void
print_tcp(struct tcphdr *tcph)
{
	printf("[TCP HEADER] src port: %5u dest port : %5u  ", ntohs(tcph->source), ntohs(tcph->dest));

	(tcph->urg == 1)?printf("U"):printf("-");
	(tcph->ack == 1)?printf("A"):printf("-");
	(tcph->psh == 1)?printf("P"):printf("-");
	(tcph->rst == 1)?printf("R"):printf("-");
	(tcph->syn == 1)?printf("S"):printf("-");
	(tcph->fin == 1)?printf("F"):printf("-");
	printf("\n\n");
}

 

 

 

IP 헤더를 프린트 하는 함수를 보면 IP 버전, IP 헤더 length, 프로토콜 종류와 도착, 출발 Ip 주소를 출력한다.

TCP 헤더를 프린트 하는 함수를 보면 출발지 포트번호와 도착지 포트 번호를 출력하며 컨트롤 비트들에 관해 출력한다.

 

무차별 모드라면 dst가 본인 아이피가 아니더라도 수집하게 될 것이다.

[IP HEADER] 버전, 헤더길이, src IP, dst IP

[TCP HEADER] src port, dest port, 컨트롤 비트

 

 

14~15번 줄을 보면 자신의 아이피 주소와 포트 번호를 설정하고 있다.

11~12번 줄을 보면 상대 서버의 스캔할 포트번호를 확인할 수 있다. 80~90 사이의 포트번호이다.

35번 째 줄을 통해서 명령어와 도메인 주소를 함깨 입력해야 함을 알 수 있다.

39번째 줄을 보면 도메인 주소를 target에 저장하고 있다. 

48번째 줄을 보면 타켓 도메인 주소의 포트번호 80~90를 스캔하기 위해 반복문을 활용하고 있음을 확인할 수 있다.

scan_syn_port()함수를 살펴보겠다.

113~123번째 줄을 살펴보면 IP 헤더에 필요한 정보를 IP 헤더 구조체에 할당하고 있다. 

123번째 줄은 체크섬을 만들고 있는 것을 확인할 수 있다.

130번째 줄에 sendto를 통해 원격지로 데이터를 전송한다. SYN 메시지를 전송하는 것이다.

 

141번째 줄에서 recvfrom을 통해서 SYN_ACK이 오길 기다린다. 

147번째 줄과 149에서 상대방이 나에게 보낸 SYN_ACK인지 확인한다. 

158번째에서 SYN값과 ACK 값을 확인한다.

 

위 이미지에서 체크섬에 관한 내용도 확인해 볼 수 있다.

 

위 실행 결과를 통해서 80번 포트 정보를 스캔하고있는 2번째 줄을 보면 마지막에 port[80] open/http를 확인할 수 있다.

이 데이터는 해당 포드에서 제공하는 서비스이다. 나머지 포트를 보면 *모양이 나타나있는 것을 확인할 수 있다. 이는 해당 포트는 열려있지 않다는 것이다. 

위 정보는 SYN 패킷을직접 만들고 조정하고 SYN_ACK 패킷을 찾아내고 조사해서 얻은 결과라 할 수 있다.

 

scan_syn_port(unsigned long target, int port)
{
	int	sd;
	int	on = 1;
	int	len;

	int	tx_packet_size = sizeof(struct iphdr) + sizeof(struct tcphdr) + sizeof(struct pseudohdr);
	int	rx_packet_size = sizeof(struct iphdr) + sizeof(struct tcphdr);
	char	*rx_packet = (char *)malloc(rx_packet_size);
	char	*tx_packet = (char *)malloc(tx_packet_size);

	struct tcphdr		*tcph, *rx_tcph;
	struct iphdr		*iph, *rx_iph;
	struct pseudohdr	 *pseudoh;

	struct in_addr		s_addr, d_addr;
	struct sockaddr_in	local, remote;

	struct servent		*serv;

	iph = (struct iphdr *)(tx_packet);
	tcph = (struct tcphdr *)(tx_packet + sizeof(struct iphdr));
	pseudoh = (struct pseudohdr *)(tx_packet + sizeof(struct iphdr) + sizeof(struct tcphdr));
	78 
	if((sd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
		printf("socket open error\n"); exit(-1);
	}

	if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) < 0) {
		printf("set socket option error\n"); exit(-2);
	}

	memset(tx_packet, 0, tx_packet_size);

	d_addr.s_addr = target;
	s_addr.s_addr = inet_addr(LOCAL_IP);

	pseudoh->s_addr = s_addr.s_addr;
	pseudoh->d_addr = d_addr.s_addr;
	pseudoh->protocol = IPPROTO_TCP;
	pseudoh->zero = 0;
	pseudoh->length = htons(sizeof(struct tcphdr));

	tcph->source = htons(LOCAL_PORT);
	tcph->dest = htons(port);
	tcph->seq = htons(random()%time(NULL));
	tcph->ack_seq = 0;
	tcph->doff = 5;
	tcph->res1 = 0;
	tcph->fin = 0;
	tcph->syn = 1;
	tcph->rst = 0;
	tcph->psh = 0;
	tcph->ack = 0;
	tcph->urg = 0;
	tcph->window = htons(1024);
	tcph->check = (unsigned short)cksum_in((unsigned short *)tcph, (sizeof(struct tcphdr) + sizeof(struct pseudohdr)));

	iph->ihl = 5;
	iph->version = 4;
	iph->tos = 0;
	iph->tot_len = htons(tx_packet_size) - sizeof(struct pseudohdr);
	iph->id = 0;
	iph->frag_off = 0;
	iph->ttl = IPDEFTTL;
	iph->protocol = IPPROTO_TCP;
	iph->saddr = s_addr.s_addr;
	iph->daddr = d_addr.s_addr;
	iph->check = (unsigned short)cksum_in((unsigned short *)iph, sizeof(struct iphdr));

	remote.sin_family = PF_INET;
	remote.sin_addr   = d_addr;
	remote.sin_port   = htons(port);
	remote.sin_port   = 0;

		if(sendto(sd, tx_packet, (tx_packet_size - sizeof(struct pseudohdr)), 0x0, (struct sockaddr *)&remote, sizeof(remote)) < 0) {
			printf("send error\n"); exit(-3);
		}
		printf("[tx] %u->%u ", ntohs(tcph->source), ntohs(tcph->dest));
		(tcph->urg == 1)?printf("U"):printf("-");
		(tcph->ack == 1)?printf("A"):printf("-");
		(tcph->psh == 1)?printf("P"):printf("-");
		(tcph->rst == 1)?printf("R"):printf("-");
		(tcph->syn == 1)?printf("S"):printf("-");
		(tcph->fin == 1)?printf("F"):printf("-");
 
	while(recvfrom(sd, rx_packet, rx_packet_size, 0x0, (struct sockaddr *)&local, &len) >0 ) {
	
			rx_iph = (struct iphdr *)(rx_packet);
			rx_tcph = (struct tcphdr *)(rx_packet + rx_iph->ihl * 4);
	
	
			if( rx_iph->saddr != iph->daddr) continue;
	
			if((ntohs(tcph->source) == ntohs(rx_tcph->dest)) && (ntohs(tcph->dest) == ntohs(rx_tcph->source))){
				printf("[rx] %u->%u  ", ntohs(rx_tcph->source), ntohs(rx_tcph->dest));
				(rx_tcph->urg == 1)?printf("U"):printf("-");
				(rx_tcph->ack == 1)?printf("A"):printf("-");
				(rx_tcph->psh == 1)?printf("P"):printf("-");
				(rx_tcph->rst == 1)?printf("R"):printf("-");
				(rx_tcph->syn == 1)?printf("S"):printf("-");
				(rx_tcph->fin == 1)?printf("F"):printf("-");
 
				if(rx_tcph->syn == 1 && rx_tcph->ack == 1) {
					serv = getservbyport(htons(port), "tcp");
					printf(" port[%d] open/%s \n", ntohs(rx_tcph->source), serv->s_name);
				} else if (rx_tcph->rst == 1) {
					printf(" *\n");
				} else {
					printf("protocol error\n"); exit(-1);
				}
	
				break;
			}
		}
		
		close(sd);
	} 
		
	unsigned short
	cksum_in(unsigned short *addr, int len)
	{	
		unsigned long sum = 0;
		unsigned short answer = 0;
		unsigned short *w = addr;
		int	 nleft = len;
		
		while(nleft > 1) {
			sum += *w++;
			if(sum & 0x80000000)
				sum = (sum & 0xffff) + (sum >> 16);
				nleft -= 2;
		}
		
		if(nleft == 1) {
			*(unsigned char *)(&answer) = *(unsigned char *)w;
			sum += answer;
		}
		
		while(sum >> 16)
			sum = (sum & 0xffff) + (sum >> 16);
	
		return(sum==0xffff)?sum:~sum;
	}

 

떳던 오류 관련

https://devks.tistory.com/47

 

Linux. 권한설정 (Permission Denied)

파일을 업로드하는 과정에서 Permission Denied가 발생했습니다. 원인은 Upload 경로에 대한 권한 설정을 하지 않은 것. Linux는 아무래도 익숙하지 않기 때문에 권한 설정을 하면서 기본적인 부분을 정�

devks.tistory.com

su에서 빠저나가는 방법 exit

 

https://storycompiler.tistory.com/44

 

[Ubuntu/Linux] su와 su - 차이점을 정리해보기

사용자 계정으로 로그인한 후 간혹 관리자 root 권한이 필요할 때가 있습니다. 패키지를 설치하거나, 시스템 설정 파일을 수정하거나, 신규유저를 추가하거나, 다른 계정으로 파일권한을 변경하�

storycompiler.tistory.com

---------------------------------------------------------------------------------------

<소스 port_scan_sys.c>
//syn_port_scan.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <netdb.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <sys/socket.h>

#define START_PORT	80
#define END_PORT		90

#define LOCAL_IP		"203.249.39.141"	// 자신의 IP 주소로 수정해야 함
#define LOCAL_PORT	9000				// 발신 포트 번호

unsigned short cksum_in(unsigned short *, int);

struct pseudohdr {
	unsigned long s_addr;
	unsigned long d_addr;
	char	zero;
	unsigned char protocol;
	unsigned short length;
};


main(int argc, char *argv[ ])
{
	unsigned long	target;
	int				portNum;
	struct hostent	*h;

	if(argc < 2) {
		printf("usage : portscan domain_name\n");
		exit(-1);
	}

	if((target = inet_addr(argv[1])) == -1) {
		h = gethostbyname(argv[1]);
		if(!h) {
			printf("gethostbyname error\n");
			return 4;
		}
		target = ((struct in_addr*)h->h_addr)->s_addr;
	}

	for(portNum = START_PORT; portNum <= END_PORT; portNum++) {
		printf("port %d scanning..", portNum);
		scan_syn_port(target, portNum);
	}
}

scan_syn_port(unsigned long target, int port)
{
	int	sd;

	int	on = 1;
	int	len;

	int	tx_packet_size = sizeof(struct iphdr) + sizeof(struct tcphdr) + sizeof(struct pseudohdr);
	int	rx_packet_size = sizeof(struct iphdr) + sizeof(struct tcphdr);
	char	*rx_packet = (char *)malloc(rx_packet_size);
	char	*tx_packet = (char *)malloc(tx_packet_size);

	struct tcphdr		*tcph, *rx_tcph;
	struct iphdr		*iph, *rx_iph;
	struct pseudohdr	 *pseudoh;

	struct in_addr		s_addr, d_addr;
	struct sockaddr_in	local, remote;

	struct servent		*serv;

	iph = (struct iphdr *)(tx_packet);
	tcph = (struct tcphdr *)(tx_packet + sizeof(struct iphdr));
	pseudoh = (struct pseudohdr *)(tx_packet + sizeof(struct iphdr) + sizeof(struct tcphdr));
	78 
	if((sd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
		printf("socket open error\n"); exit(-1);
	}

	if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) < 0) {
		printf("set socket option error\n"); exit(-2);
	}

	memset(tx_packet, 0, tx_packet_size);

	d_addr.s_addr = target;
	s_addr.s_addr = inet_addr(LOCAL_IP);

	pseudoh->s_addr = s_addr.s_addr;
	pseudoh->d_addr = d_addr.s_addr;
	pseudoh->protocol = IPPROTO_TCP;
	pseudoh->zero = 0;
	pseudoh->length = htons(sizeof(struct tcphdr));

	tcph->source = htons(LOCAL_PORT);
	tcph->dest = htons(port);
	tcph->seq = htons(random()%time(NULL));
	tcph->ack_seq = 0;
	tcph->doff = 5;
	tcph->res1 = 0;
	tcph->fin = 0;
	tcph->syn = 1;
	tcph->rst = 0;
	tcph->psh = 0;
	tcph->ack = 0;
	tcph->urg = 0;
	tcph->window = htons(1024);
	tcph->check = (unsigned short)cksum_in((unsigned short *)tcph, (sizeof(struct tcphdr) + sizeof(struct pseudohdr)));

	iph->ihl = 5;
	iph->version = 4;
	iph->tos = 0;
	iph->tot_len = htons(tx_packet_size) - sizeof(struct pseudohdr);
	iph->id = 0;
	iph->frag_off = 0;
	iph->ttl = IPDEFTTL;
	iph->protocol = IPPROTO_TCP;
	iph->saddr = s_addr.s_addr;
	iph->daddr = d_addr.s_addr;
	iph->check = (unsigned short)cksum_in((unsigned short *)iph, sizeof(struct iphdr));

	remote.sin_family = PF_INET;
	remote.sin_addr   = d_addr;
	remote.sin_port   = htons(port);
	remote.sin_port   = 0;

		if(sendto(sd, tx_packet, (tx_packet_size - sizeof(struct pseudohdr)), 0x0, (struct sockaddr *)&remote, sizeof(remote)) < 0) {
			printf("send error\n"); exit(-3);
		}
		printf("[tx] %u->%u ", ntohs(tcph->source), ntohs(tcph->dest));
		(tcph->urg == 1)?printf("U"):printf("-");
		(tcph->ack == 1)?printf("A"):printf("-");
		(tcph->psh == 1)?printf("P"):printf("-");
		(tcph->rst == 1)?printf("R"):printf("-");
		(tcph->syn == 1)?printf("S"):printf("-");
		(tcph->fin == 1)?printf("F"):printf("-");
 
	while(recvfrom(sd, rx_packet, rx_packet_size, 0x0, (struct sockaddr *)&local, &len) >0 ) {
	
			rx_iph = (struct iphdr *)(rx_packet);
			rx_tcph = (struct tcphdr *)(rx_packet + rx_iph->ihl * 4);
	
	
			if( rx_iph->saddr != iph->daddr) continue;
	
			if((ntohs(tcph->source) == ntohs(rx_tcph->dest)) && (ntohs(tcph->dest) == ntohs(rx_tcph->source))){
				printf("[rx] %u->%u  ", ntohs(rx_tcph->source), ntohs(rx_tcph->dest));
				(rx_tcph->urg == 1)?printf("U"):printf("-");
				(rx_tcph->ack == 1)?printf("A"):printf("-");
				(rx_tcph->psh == 1)?printf("P"):printf("-");
				(rx_tcph->rst == 1)?printf("R"):printf("-");
				(rx_tcph->syn == 1)?printf("S"):printf("-");
				(rx_tcph->fin == 1)?printf("F"):printf("-");
 
				if(rx_tcph->syn == 1 && rx_tcph->ack == 1) {
					serv = getservbyport(htons(port), "tcp");
					printf(" port[%d] open/%s \n", ntohs(rx_tcph->source), serv->s_name);
				} else if (rx_tcph->rst == 1) {
					printf(" *\n");
				} else {
					printf("protocol error\n"); exit(-1);
				}
	
				break;
			}
		}
		
		close(sd);
	} 
		
	unsigned short
	cksum_in(unsigned short *addr, int len)
	{	
		unsigned long sum = 0;
		unsigned short answer = 0;
		unsigned short *w = addr;
		int	 nleft = len;
		
		while(nleft > 1) {
			sum += *w++;
			if(sum & 0x80000000)
				sum = (sum & 0xffff) + (sum >> 16);
				nleft -= 2;
		}
		
		if(nleft == 1) {
			*(unsigned char *)(&answer) = *(unsigned char *)w;
			sum += answer;
		}
		
		while(sum >> 16)
			sum = (sum & 0xffff) + (sum >> 16);
	
		return(sum==0xffff)?sum:~sum;
	}
</소스>

 

 

 

 

 

raw 뜻 : 디지털 분야에서 RAW는 '날것의'라는 뜻의 raw라는 영어 형용사의 의미가 확장되어 쓰이는 전문 용어이다. 데이터 압축 뿐만 아니라 특정 프로토콜에 맞추기 위해 가공을 거치지 않은 디지털 데이터를 뜻하는 용어이다.

https://namu.wiki/w/RAW(%ED%8C%8C%EC%9D%BC)

 

RAW(파일) - 나무위키

RAW 이미지 및 영상 파일에는 이미지 프로세서에서 최소한의 후처리만 거친 색상 데이터와 촬영 당시에 ISO 감도, 조리개값, 노출 시간, 렌즈 정보 등이 포함된 메타데이터가 들어있다. 또한, RAW ��

namu.wiki

 

일반 소켓 같은 경우 write(socket, buffer, stream) 이런 식으로 데이터 크기정도만 조작할 수 있다. 하지만 raw 소켓 같은 경우는 응용 프로그램에서 직접 프로토콜 헤더의 전부나 일부를 생성하거나 읽고 쓸 수 있다.

 

raw 소켓을 사용하는 이유는 새로운 프로토콜을 생성하거나 소켓을 새밀하게 조작하기 위해서이다. 

 

 

 

특징 요약

- 세부적인 조작

- 해킹 등에 사용되기 때문에 오해 받지 않도록 세심한 주의 요청

- 신규 프로토콜 설계시 유용

 

Raw 소켓의 생성

#include <netinet/in.h>

 

int sd;

sd = socket(AF_INET, SOCK_RAW, protocol);

 

 

지금 까지는 tcp헤더를 조작하는 방법을 살펴봤다.

이번에는 IP 헤더를 조작하기 위한 방법이다. IP 헤더를 조작하기 위해서는 IP_HDRINCL 값을 on 상태로 두어야 된다.

 

 

promiscuous -> 무차별 

 

Promiscuous Mode 로 설정된 경우 자신의 맥주소와 다른 소켓도 가져오게 된다. lan에 흘러다니는 모든 소켓을 카피해버린다. 

 

LAN 카드는 맥주소를 보게된다. 자신의 맥주소랑 같은 것을 카피한다. 이때 소켓이 일반 소켓이면 IP 모듈로 보내게 된다.

IP 모듈은 패킷 헤더를 열어서 IP 주소를 확인하게 된다. 자신의 IP와 같다면 그 모듈을 TCP 모듈이나 UDP 모듈로 전송한다.

UDP 모듈 또는 TCP 모듈은 포트를 확인하게 된다. 포트를 통해 무슨 서비스를 이용하는지 확인 가능하며 일반 소켓으로 올라가게 된다. 

이때 raw 소켓을 사용하게 되면 IP 모듈 TCP, UDP 모듈 소켓 모듈을 거치지 않고 모조리 raw 소켓으로 바로 올려버린다.

 

raw 소켓은 자신의 맥주소와 상관없이 소켓을 다 모니터링 해볼 수 있다.

 

 

 

리눅스에서 ifconfig eth0 promisc 명령어를 통해서 무차별 모드를 활성화 할 수 있다.

여기서 et0 는 랜카드 번호이다. 

 

무차별 모드로 설정된 예제가 있다. 일단 일반 소켓 영역에서 말하자면 아래 그림을 보면 NIC(Network Interface Card) 에서 자신의 MAC주소와 같은 

 

 

SYN 메시지를 주고밭는 이유는 서버 포트가 열려 있는지 판단하기 위해서이다. 

https://www.youtube.com/watch?v=p3giFcDeCjE&list=PL7mmuO705dG265DxvC-oZgKViaRGt2-eS&ab_channel=%ED%94%84%EB%A6%AC%EB%A0%89

 

알아야 하는 것들

nm = nanometre

1nm = 10^-9m

 

기본 지식으로 참고한 영상

https://brunch.co.kr/@mascott/18

 

광케이블(Optical Fiber Cable)의 종류

광케이블의 세계로... | ◎ 광케이블(Optical Cable) ▸ 광신호로 변환된 레이저 신호를 멀리까지 전송하기 위해서 광섬유로 만들어진 전송 케이블 ▸ 광케이블은 구리선을 사용하는 통신방식보다

brunch.co.kr

광 케이블 모드

1. 싱글 모드

- 광케이블 코어 안에 토파되는 빛의 모드가 하나인 광섬유

- 장거리 고속 전송에 유리하다. (현재 신규로 구축되는 광선로에서는 single mode 케이블만 대부분 사용한다) 

2. 멀티 모드

- 광케이블 코어안에 3개의 빛이 사용되는 모드고 코어의 직경이 크다.

- 단거리 전송에 유리

 

 

 

 

참고 영상 

https://www.youtube.com/watch?v=cKCst1wccfo

 

SFP는 Optical Signal(광 신호) 를 Electrical Signal(전기 신호)로 변환해주는 역할을 한다.

SFP는 종류가 여러가지인데 종류는 wavelength(주파수) 와 거리 그리고 싱글 모드인지 멀티 모드인지에 따라 달라진다.

 

 

 

짧은 거에서는 1000BASE-T 그리고 1000BASE-SX SFP가 사용되는 것 같다. 1000BASE-T는 SFP가 붙어있지 않으니 광 케이블을 사용하지 않는 것 같다.

 

duplex : 이중

 

SX short-range

LX long-range 10km

EX 40km 

ZX 80km 

 

bidirectional : 양방향

BiDi SFP는 항상 같은 주파수를 가진 transceiver를 사용해야 한다.

 

type distance  wavelength mode
1000BASE-T 100M(Cat5)    
1000BASE-SX 550M 850nm multi
1000BASE-LX SFP 10KM 1310nm single, duplex LC cables
1000BASE-EX SFP 40KM   single, duplex LC cables
1000BASE-ZX SFP 80KM   single, duplex LC cables
BiDi SFP(one fiber)      

 

전송 속도에 따른 SFP 분류

종류 전송 속도
SFP 1~2.5Gbps
SFP+ 10Gbps
SFP28 25Gbps
QSFP+ 40Gbps
CFP 100Gbps

QSFP+ 는 전송속도가 40기가나 되니 크기도 크다.

 

 

<WDM>

저비용에 네트워크 capacity의 증가에 따라 WDM이라는 기술이 탄생

 

WDM 에는 CWDM SFPs 그리고 DWDM SFPs가 있다.

 

CWDM은 80KM 까지 지원 및 1270nm ~ 1610nm 주파수 대역을 사용한다.

 

DWDM 80~200KM 까지 가능하며 주파수폭이 작아지고 수가 많아졌다.

 

우분투를 통해 통신하는 프로그램을 살펴보던 중 이런 화면을 보게 되었다.

정보처리기사 자격증을 공부하던 중 한번 본 내용(ARP)이었지만 암기식으로 학습해서인지 생각이 나질 않아서 블로그 검색을 해야 내용을 이해하게 되었다.

 

ARP 프로토콜은 간단하게 말하자면 IP를 통해 MAC address를 알아내는 것이다. 

구체적으로 ARP 프로토콜에 대해 말해보겠다.

위 화면 내용중 아래와 같은 내용이 있다.

  Destination: Broadcast (ff:ff:ff:ff:ff:ff)

      Address: Broadcasst (ff:ff:ff:ff:ff:ff)

이는 src 단말 장치에서 망에 연결된 모든 단말 장치에게  연락을 하는 것이다. 

src 단말 장치에서 무슨 목적으로 브로드캐스트 메시지를 보내는가는 아래를 보면 알 수 있다.

Address Resolution Protocol (request/gratuitous ARP)

즉 IP 주소로 메시지를 보내기 위해 MAC 주소를 알아내기 위함이다.

그리고 자세히 보면 괄호에 gratuitous ARP 라는 구문이 있다.

gratuitous라는 의미는 쓸데없는 라는 의미가 있다. 

위 이미지를 다시 살펴보면 sender IP와 target IP가 같음을 알 수 있다. 따라서 MAC 주소를 확인할 필요가 없는

쓸데없는 ARP 라는 것이다. 그럼에도 이는 의미가 있는데 3가지 용도가 있다.

 

1) IP 주소 충돌 감지 -> 자신의 IP주소를 타깃으로 하여 ARP 요청을 보냈는데 만약 다른 호스트에서 이에 대한 응답이 있다면 IP 충돌을 감지한다.

2) ARP Table 갱신 -> 자신의 IP를 가지고있는 단말 장치가 있다면 MAC 주소를 갱신한다.

3) VRRP/HSRP -> VRRP/HSRP가 enable된 라우터 중 Master에 해당하는 라우터에 해당하는 자신과 연결된 L2 switch의 Mac address table 정보를 갱신(해당 MAC 주소에 대한 Port 정보 갱신) 하기 위해 GARP를 사용한다.

 

참조

https://www.netmanias.com/ko/post/blog/5402/arp-ethernet-ip-ip-routing-network-protocol/arp-and-garp-gratuitous-arp

 

ARP와 GARP(Gratuitous ARP)

IPoE(IP over Ethernet) 환경에서(현재 우리 모두가 사용하고 있는 환경이죠... L3 = IP, L2 = Ethernet) 상대방 호스트(PC/서버) 혹은 라우터의 MAC 주소를 알기 위해, 해당 노드(호스트/라우터)의 IP 주소를 타��

www.netmanias.com

https://blockdmask.tistory.com/189

 

[데통] ARP protocol 에 대해서

안녕하세요. BlockDMask입니다. 오늘은 ARP 프로토콜에 대해 알아보겠습니다. > ARP 프로토콜 이란? ARP Protocol (Address Resolution Protocol - 주소 결정 프로토콜) - 네트워크 상에서 IP 주소를 물리적 네..

blockdmask.tistory.com

 

c언어에서는 기본적으로 1,2,4,8바이트 순으로 2의 지수가 증가하는 흐름으로 값을 저장할 수 있는 변수가 있습니다.

하지만 필요에 따라 3바이트 6바이트 짜리 데이터가 필요할 때도 있습니다.

그래서 이번에는 3바이트, 6바이트 데이터를 만드는 것을 해보도록 하겠습니다.

 

https://dojang.io/mod/page/view.php?id=653

 

C 언어 코딩 도장: 75.3 여러 줄을 묶어서 매크로로 정의하기

지금까지 숫자 한 개나 코드 한 줄을 매크로로 정의해서 사용했습니다. 그럼 여러 줄을 매크로로 만들 수 있지 않을까요? #define은 줄바꿈이 일어날 때 \를 사용하여 여러 줄을 매크로로 만들 수 �

dojang.io

우선 제 코드를 이해하기 위한 기본적인 지식입니다.

매크로를 여러줄 사용하기 위해서 \를 줄 끝에 가져가면 다음줄에도 매크로 정의를 이어서 사용할 수 있습니다. 마지막 줄엔 하지 않아도 됩니다.

 

<3바이트 값>

우선 integer로 값을 입력 받은후 3바이트 값에 값을 넣어보도록 하겠습니다. 

저는 3바이트 값을 uint8_t arr[3] 와 같은 식으로 구현하였습니다.

uint8_t를 모르신다면 char와 같다 생각하시면 됩니다.

여기서 static inline은 함수 속도를 아주 조금 빠르게 만드는데

네트워크에서 몇초에 수천 수만번 사용되는 함수라면 그 조금의 차이도 크기 때문에 사용합니다.

static inline void
intTo3bytes(int num, uint8_t* arr)
{
	arr[0] = (uint8_t)(num & 0x000000ff);
	arr[1] = (uint8_t)((num & 0x0000ff00) >> 8);
	arr[2] = (uint8_t)((num & 0x00ff0000) >> 16);
}

num은 3바이트에 넣을 값 그리고 arr는 3바이트 배열입니다.

4바이트로 값을 입력 받지만 가장 높은 1바이트 쪽 데이터는 버려지게 됩니다.

논리 연산자를 이용해서 각각 바이트 값을 추출하여 배열에 담는 식으로 3바이트 값에 값을 저장합니다.

 

하지만 위와 같이 3바이트 값을 저장하면 읽을 때는 읽는 방법또한 구현해 주어야합니다.

다음은 3바이트를 인티저로 바꿔서 읽는 방법을 구현한 매크로입니다.

 

#define bytes3ToInt(n1, n2, n3) ((uint32_t)(n3 & 0xff) << 16) | \
				((uint32_t)(n2 & 0xff) << 8)  | \
				((uint32_t)(n1 & 0xff)) 

위와 같이 배열에 저장된 순서에 따라 값의 순서를 8바이트 부터 그리고 16바이트부터 준 다음 논리 연산자로 합치는 매크로를 통해서 3바이트 값을 정수처럼 읽을 수 있도록 만들어 보았습니다.

 

그리고 이번에는 6바이트 입니다 3바이트를 만들 수 있으면 6바이트는 거져먹기죠

 

#define bytes6ToInt(n1, n2, n3, n4, n5, n6) ((uint64_t)(n6 & 0xff) << 48)  | \
						((uint64_t)(n5 & 0xff) << 32)  | \
						(uint64_t)(n4 & 0xff) << 24)   | \
						((uint64_t)(n3 & 0xff) << 16)  | \
						((uint64_t)(n2 & 0xff) << 8)   | \
						((uint64_t)(n1 & 0xff)) 

 

static inline void
longTo6bytes(long long num, uint8_t* arr)
{
	arr[0] = (uint8_t)(num & 0x00000000000000ff);
	arr[1] = (uint8_t)((num & 0x000000000000ff00) >> 8);
	arr[2] = (uint8_t)((num & 0x0000000000ff0000) >> 16);
	arr[3] = (uint8_t)((num & 0x00000000ff000000) >> 24);
	arr[4] = (uint8_t)((num & 0x000000ff00000000) >> 32);
	arr[5] = (uint8_t)((num & 0x0000ff0000000000) >> 40);
}

매크로로 만들고 보니 실제 배열 하나하나 저렇게 값을 넣는건 불편하니 매크로를 활용한 함수를 사용하는게 좋을 것 같아서 추가적으로 소개하고 끝내겠습니다.

 

static inline uint64_t
bytes6Tolong(uint8_t * arr)
{
	uint64_t num = _bytes6ToInt(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5]);
	return num;
}

static inline uint32_t
bytes3ToInt(uint8_t* arr)
{
	uint32_t num = _bytes3ToInt(arr[0], arr[1], arr[2]);
	return num;
}

 

 

이번 기회에 패킷을 네트워크로 보낼 때 사용하는 htonl에 대해 이야기하려 합니다.

여러분도 알듯이 이는 엔디안 타입을 빅 엔디안에서 리틀 엔디안으로 바꾸는 함수입니다.

htonl 나 htons 함수를 사용하려면 ws2_32.lib 함수가 필요합니다.

헤더 부분에 

#include <winsock2.h>
#pragma comment(lib, "ws2_32")

추가 해주시면 됩니다. 

 

winsock 헤더 파일에 htons(2 bytes)나 htonl(4 bytes) 이 포함돼있는 것으로 알고 있습니다. 

하지만 저는 8바이트 데이터도 보내야 되는 상황이였는데 지원하지 않는군요

그래서 직접 만들어 보았습니다.

 

uint64_t hton64(uint64_t num)
{
	uint8_t arr[8];
	uint8_t temp[8];
	uint64_t after=0;
	arr[0] = (uint8_t)(num & 0x00000000000000ff);
	arr[1] = (uint8_t)((num & 0x000000000000ff00) >> 8);
	arr[2] = (uint8_t)((num & 0x0000000000ff0000) >> 16);
	arr[3] = (uint8_t)((num & 0x00000000ff000000) >> 24);
	arr[4] = (uint8_t)((num & 0x000000ff00000000) >> 32);
	arr[5] = (uint8_t)((num & 0x0000ff0000000000) >> 40);
	arr[6] = (uint8_t)((num & 0x00ff000000000000) >> 48);
	arr[7] = (uint8_t)((num & 0xff00000000000000) >> 56);
	int i,k;
	for (i = 0; i < 8; i++)
	{
		temp[i] = arr[i];
	}
	for (i = 0, k=7; i < 8 && k>0; i++, k--)
	{
		arr[i] = temp[k];
	}
	after = ((uint64_t)arr[0]) | (((uint64_t)arr[1]) << 8) | (((uint64_t)arr[2]) << 16)
		| (((uint64_t)arr[3]) << 24) | (((uint64_t)arr[4]) << 32) | (((uint64_t)arr[5]) << 40)
		| (((uint64_t)arr[6]) << 48) | (((uint64_t)arr[7]) << 56);
	return after;


}

이런 식으로 8바이트 데이터도 엔디안 타입을 변경 할 수 있도록 하였습니다.

1바이트 8개 인수 배열에 값을 담고 배열 순서를 뒤집은 다음 다시 저장하고

그 값을 논리 연산자로 합 처서 8바이트로 다시 만드는 함수입니다.

 

이 함수를 제작한 뒤 이번엔 ntoh64 함수를 만들려는 중 의문이 들었습니다.

빅 엔디안에서 리틀 엔디안으로 가는 함수나 리틀 엔디안에서 빅 엔디안으로 가는 함수는 읽는 순서가 바뀌는 것이기 때문에 구분할 필요 없이 그냥 순서만 바꿔주면 되지 않을까라는 생각이 들었습니다. 

그래서 ntoh64 함수는 아래와 같이 만들어 봤습니다.

uint64_t ntonh64(uint64_t num)
{
	uint8_t arr[8];
	uint8_t temp[8];
	uint64_t after = 0;
	arr[0] = (uint8_t)(num & 0x00000000000000ff);
	arr[1] = (uint8_t)((num & 0x000000000000ff00) >> 8);
	arr[2] = (uint8_t)((num & 0x0000000000ff0000) >> 16);
	arr[3] = (uint8_t)((num & 0x00000000ff000000) >> 24);
	arr[4] = (uint8_t)((num & 0x000000ff00000000) >> 32);
	arr[5] = (uint8_t)((num & 0x0000ff0000000000) >> 40);
	arr[6] = (uint8_t)((num & 0x00ff000000000000) >> 48);
	arr[7] = (uint8_t)((num & 0xff00000000000000) >> 56);
	int i, k;
	for (i = 0; i < 8; i++)
	{
		temp[i] = arr[i];
	}
	for (i = 0, k = 7; i < 8 && k>0; i++, k--)
	{
		arr[i] = temp[k];
	}
	after = ((uint64_t)arr[0]) | (((uint64_t)arr[1]) << 8) | (((uint64_t)arr[2]) << 16)
		| (((uint64_t)arr[3]) << 24) | (((uint64_t)arr[4]) << 32) | (((uint64_t)arr[5]) << 40)
		| (((uint64_t)arr[6]) << 48) | (((uint64_t)arr[7]) << 56);
	return after;


}

맞습니다. 이름만 바꿨고 기능은 같습니다. 리틀 엔디안에서 빅 엔디안으로 가든 빅 엔디안에서 리틀 엔디안으로 가든 읽는 순서만 반대로 해주면 그만이기 때문에 내용은 바뀔 필요 없이 이름만 바꾸면 될 것 같습니다.  그래서 위와 같이 작성하였습니다.

 

그리고 구글링을 하던중

uint64_t hton64(uint64_t n)
{
	int backV = (int)(n & 0x00000000ffffffff);
	int frontV = (int)((n & 0xffffffff00000000) >> 32);
	uint64_t temp = (((uint64_t)htonl(frontV)) << 32) | ((uint64_t)htonl(backV));
	return temp;
}

 이런 느낌의 함수를 봤었는데 처음에는 그냥 쓰다가 문득 의문이 떠올랐습니다.

8바이트짜리 엔디안 변경 함수가 없으니 4바이트로 쪼갠다음 각각 htonl을 적용하는 함수였습니다.

하지만 이렇게 하면 문제가 생기지 않을까 생각합니다.

엔디안 변경 시 12345678의 값을 87654321로 변경해야 합니다.

하지만 위와 같이 사용하게 되면 43218765 이런 식으로 변경될 것이라 생각합니다.

엔디안 타입을 변경하길 원했지만 의미 없는 값이 나오게 되는 것입니다.

 

마지막으로 3바이트 htonl도 만들어 보았습니다.

#define hton24(n1, n2, n3)  ((uint32_t)(((n3 & 0xff) << 16) | ((n2 & 0xff) << 8) | (n1 & 0xff)))
#define ntoh24(n1, n2, n3)  ((uint32_t)(((n3 & 0xff) << 16) | ((n2 & 0xff) << 8) | (n1 & 0xff)))

저 매크로를 이용하여 함수를 만들어서 배열을 입력하지 않고 사용할 수도 있을 것입니다.

 

혹시 제가 쓴 글에 뭔가 추가해야 할 사항이 있다면 알려주세요

후기) htonll이란 메소드가 존재합니다. 이 메소드는 8바이트 변수값 변경을 지원합니다. 

 

 

  

소켓 통신을 하기위하여 기본적인 라이브러리 추가 방법을 모른다면 이전 글을 참조해주세요

 

위 이미지처럼 결과가 나오게끔 코드를 공개하겠습니다.

 

클라이언트 코드

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS

#include <WinSock2.h>
#include <stdlib.h>
#include <stdio.h>

#define BUFSIZE 512

int main(int argc, char* argv[]) {
	int retval;

	// 윈속 초기화
	WSADATA wsa;
	if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
		return -1;

	// socket()
	SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
	if (sock == INVALID_SOCKET) return -1;
	printf("UDP 소켓이 생성되었습니다.\n");

	// 소켓 주소 구조체 초기화
	SOCKADDR_IN serveraddr;
	ZeroMemory(&serveraddr, sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(9000);
	serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");

	// 데이터 통신에 사용할 변수
	SOCKADDR_IN peeraddr;
	int addrlen;
	char buf[BUFSIZE + 1];
	int len;
	for (int i = 0; i < 5; i++) {
		// 데이터 입력
		printf("\n[보낼 데이터]");
		fgets(buf, BUFSIZE + 1, stdin); // buf 에 BUFSIZE +1 만큼의 크기를 입력 buf는 배열의 시작 주소를 가르킨다. 
		// 배열의 이름 = 배열의 시작 주소
		//stdin -> 키보드 입력 사용

		// '\n' 문자 제거
		len = strlen(buf);
		if (buf[len - 1] == '\n')
			buf[len - 1] = '\0';

		// printf("buf: %s\n", buf);


			// 데이터 보내기
		retval = sendto(sock, buf, strlen(buf), 0, (SOCKADDR*)&serveraddr, sizeof(serveraddr));
		if (retval == SOCKET_ERROR) return -1;
		printf("[클라이언트] %d바이트를 보냈습니다.\n", retval);

		// 데이터 받기
		addrlen = sizeof(peeraddr);
		retval = recvfrom(sock, buf, BUFSIZE, 0,
			(SOCKADDR*)&peeraddr, &addrlen);
		printf("서버로 부터 데이터를 받았습니다. \n\n");

		// 송신자의 IP 주소 체크
		if (memcmp(&peeraddr, &serveraddr, sizeof(peeraddr)))
		{
			printf("[오류] 잘못된 데이터 입니다!\n");
			return -1;
		}

		//받은 데이터 출력
		buf[retval] = '\0';
		printf("[클라이언트] %d바이트를 받았습니다.\n", retval);
		printf("[받은 데이터] %s\n", buf);
	}

	// closesocket()
	closesocket(sock);

	// 윈속 종료
	WSACleanup();
	return 0;
}

 

서버 코드

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS

#include <WinSock2.h>
#include <stdlib.h>
#include <stdio.h>


#define BUFSIZE 512

int main(int argc, char* argv[])
{
	int retval;

	// 윈속 초기화
	WSADATA wsa;
	if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
		return -1;

	// socket()
	SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
	if (sock == INVALID_SOCKET) return -1;
	printf("UDP 소켓이 생성되었습니다.\n");

	// bind()
	SOCKADDR_IN serveraddr;
	ZeroMemory(&serveraddr, sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(9000); // host(컴퓨터가 데이터 읽는 방식) 방식을 network방식으로 변경
	serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
	retval = bind(sock, (SOCKADDR*)&serveraddr, sizeof(serveraddr));

	if (retval == SOCKET_ERROR) return -1;
	printf("Bind 완료 되었습니다.\n");

	//데이터 통신에 사용할 변수
	SOCKADDR_IN clientaddr;
	int addrlen;
	char buf[BUFSIZE + 1];

	for (int i = 0; i < 5; i++) {
		// 데이터 받기
		addrlen = sizeof(clientaddr);
		retval = recvfrom(sock, buf, BUFSIZE, 0,
			(SOCKADDR*)&clientaddr, &addrlen);
		printf("Client로 부터 데이터를 받았습니다.\n");

		// 받은 데이터 출력
		buf[retval] = '\0';
		printf("[UDP/%s:%d] %s\n", inet_ntoa(clientaddr.sin_addr),
			ntohs(clientaddr.sin_port), buf);

		// 데이터 보내기
		retval = sendto(sock, buf, retval, 0,
			(SOCKADDR*)&clientaddr, sizeof(clientaddr));
		printf("Client로 데이터를 전송했습니다.");
	}

	// closesocket()
	closesocket(sock);

	// 윈속 종료
	WSACleanup();
	return 0;
}

우선 소켓 통신을 하기에 앞서 사용해야할 라이브러리를 사용할 수 있도록 설정하겠습니다.

 

비주얼 스튜디오 프로젝트 생성을 했다면, 우측 상단 탭에서 프로젝트 -> 속성 클릭

구성속성 -> 고급에서

문자 집합을 멀티바이트 문자 집합 사용으로 변경

 

다음으로, 링커 -> 입력으로 들어가 추가 종속성을 추가한다.

 

라이브러리를 추가하는 방식이 번거롭다면 임의 위치에 아래 코드를 넣어서 사용해도 됩니다.

#pragma comment(lib, "ws2_32");

를 넣어주면 소켓 통신 기본 준비는 끝납니다.

 

서버와 클라이언트는 각각의 프젝트로 생성하는 것을 추천드립니다.

 

 

서버 소스코드

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS

#include <winsock2.h>
#include<stdio.h>
#include<stdlib.h>
#pragma comment(lib, "ws2_32");

#define BUFSIZE 512

void err_quit(const char* msg) {
	LPVOID IpMsgBuf;
	FormatMessage(
		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
		NULL, WSAGetLastError(),
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		(LPTSTR)&IpMsgBuf, 0, NULL);
	exit(-1);

}

int main(int argc, char* argv[]) {
	int retval;

	//원속초기화
	WSADATA wsa;
	if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
		return -1;
	//MessageBox(NULL, "원속 초기화 성공", "알림", MB_OK);

	//socket()
	SOCKET listen_sock = socket(AF_INET, SOCK_STREAM, 0);
	if (listen_sock == INVALID_SOCKET) err_quit("socket()");
	printf("소켓이 생성되었습니다\n");
	//MessageBox(NULL, "TCP 소켓성공", "알림", MB_OK);

	//bind()
	SOCKADDR_IN serveraddr;
	ZeroMemory(&serveraddr, sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
	serveraddr.sin_port = htons(9000);

	bind(listen_sock, (SOCKADDR*)&serveraddr, sizeof(serveraddr));
	printf("Bind 완료 되었습니다.\n");

	//listen()
	listen(listen_sock, SOMAXCONN);
	printf("connect 연결을 기다리는 중..\n");

	//통신에 사용할 변수
	SOCKET client_sock;
	SOCKADDR_IN clientaddr;
	int addrlen;
	char buf[BUFSIZE + 1];

	//accept()
	addrlen = sizeof(clientaddr);
	client_sock = accept(listen_sock, (SOCKADDR*)&clientaddr, &addrlen);
	printf("요청을 받았습니다.\n");

	//recv()
	retval = recv(client_sock, buf, BUFSIZE, 0);
	printf("메세지를 수신하였습니다.\n");

	//받는 데이터 출력
	buf[retval] = '\0';
	printf("[TCP/%s:%d] %s\n",
		inet_ntoa(clientaddr.sin_addr),
		ntohs(clientaddr.sin_port), buf);
	//closesocket
	closesocket(listen_sock);

	//원속 종료
	WSACleanup();
	return 0;

}

 

클라이언트 소스코드

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<stdlib.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32");

#define BUFSIZE 512

void err_quit(const char* msg) {
	LPVOID IpMsgBuf;
	FormatMessage(
		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
		NULL, WSAGetLastError(),
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		(LPTSTR)&IpMsgBuf, 0, NULL);
	exit(-1);

}

int main(int argc, char* argv[]) {
	//원속 초기화
	WSADATA wsa;
	if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
		return -1;
	//MessageBox(NULL, "원속 초기화 성공", "알림", MB_OK);

	//socket()
	SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
	if (sock == INVALID_SOCKET) err_quit("socket()");
	printf("소켓이 생성되었습니다.\n");
	//MessageBox(NULL, "TCP 소켓성공", "알림", MB_OK);

	//connect()
	SOCKADDR_IN serveraddr;
	ZeroMemory(&serveraddr, sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	serveraddr.sin_port = htons(9000);

	connect(sock, (SOCKADDR*)&serveraddr, sizeof(serveraddr));
	printf("connect 연결요청!!\n");

	//서버로 보낼 스트링 설정
	char buf[BUFSIZE + 1];
	sprintf(buf, "hello world!");

	//send()
	send(sock, buf, strlen(buf), 0);
	printf("메세지를 보냅니다.\n");

	//closesocket()
	closesocket(sock);

	//원속 종료
	WSACleanup();
	return 0;

}

 

간단하게 hello world를 클라이언트가 서버에게 보내는 코드였습니다. 

 

 

https://www.netmanias.com/ko/?m=view&id=techdocs&no=5138

 

LTE 네트워크 구조

LTE 분야 첫 문서로 LTE 네트워크 구조를 소개한다. 먼저 LTE 네트워크 참조 모델을 정의하고 LTE 네트워크를 구성하는 기본적인 Evolved Packet System(EPS) 엔터티들과 각 엔터티 기능을 기술한다. 이어서

www.netmanias.com

위 사이트에서 배운 내용을 정리한 글입니다.

 

 

LTE 네트워크는 무선 접속망(E-UTRAN) 관련 기술을 다루는 LTE 부분과 Core 망 관련 기술을 다루는 EPC 부분으로 나누어지고 LTE와 EPC를 통합하여 EPS라 일컫는다. 

LTE 네트워크는 E2E all-IP 네트워크 트래픽 흐름은 모두 IP 기반으로 동작한다.

 

사용자 평면(User Plane) : 데이터 평면이라고도 불리는 사용자 평면은 네트워크 사용자 트래픽을 전달한다.

 

제어 평면(control plane): 신호 트래픽을 전달하는 네트워크의 일부분이며 라우팅을 담당한다. 제어 패킷은 라우터에서 발생하거나 라우터로 지정된다. 제어면의 기능은 시스템 구성과 관리를 포함한다.

 

AS : Acess Stratum  액세스 계층

액세스 계층은 무선 네트워크와 사용자 장비 사이의 UMTS 및 LTE 무선 통신 프로토콜 스택의 기능 계층입니다. 액세스 계층의 정의는 UMTS와 LTE간에 매우 다르지만, 두 경우 모두 액세스 계층은 무선 연결을 통해 데이터를 전송하고 무선 리소스를 관리합니다.

startum : 계층, 지층, Layer 보다 이의 상위개념으로 사용되는 용어

 

BSS : Business Support System  

사업 관리 시스템은 통신 서비스 제공자가 고객들을 대상으로 사업 운영을 수행하기 위해 사용하는 요소들이다. 운용 지원 시스템과 더불어 다양한 단대단 통신 서비스를 지원하기 위해 사용된다. BSS와 OSS는 자신만의 데이터 및 서비스 책임이 있다. from 위키백과

 

CDR : Charging Data Record  청구 데이터 기록

CDR은 통신에서 청구 가능한 통신 이벤트에 대한 정보의 형식화된 모음이다. CDR은 사용자 청구에 사용된다.

 

DL : Downlink

위성에서 지상국 방향의 링크이다

 

DRB : Data Radio Bearer

베어러 (Bearer) - 전달/전송의 운반체, 가상 컨테이너 등을 의미

bearer : 열매 맺는 초목

 

E2E : End to End

 

ECM : EPS Connection Management

EPS Connection Management 상태는 UE와 EPC간의 신호 전달 연결을 설명합니다. ECM에는 두가지 상태, 즉 ECM-ID및 커넥터 연결됨이 있습니다.

 

EMM : EPS Mobility Management

EMM State는 이동성 관리 절차에서 비롯된다. 

EPS 이동 관리

Mobility : 유동성, 이동

 

 

EPC : Evolved Packet Core

EPC)는 3GPP LTE (Long Term Evolution)망에서의 코어 네트워크 구조를 지칭한다. EPC는 GPRS 코어 네트워크의 진화된 형태 from 위키백과

 

EPS : Evolved Packet System

 

ESM : EPS Session Management

EPS Session Management는 EPS 베어러 컨텍스트 처리를 위한 절차를 제공한다. 접근 계층에 의해 제공되는 베어러 제어와 함께, 그것은 사용자 평면 베어러의 제어를 제공한다.

 

E-UTRA : Evoloved Universal Terrestrial Radio Access 

E-UTRA는 이동통신망을 위한 3세대 파트너십 프로젝트 LTE 업그레이드 경로의 무선 인터페이스이다. 진화된 범용 지상 무선 접속 기술의 준말인데 이는 롱 텀 에볼루션의 3GPP 작업 항목이며 3GPP LTE 사양 초기 초안에서 E-UTRA로도 알려져 있다

Terrestrial : 지구의 

진화된 범용 지상파 무선 엑세스

이미지 출처: http://terms.tta.or.kr/dictionary/dictionaryView.do?word_seq=166868-9

 

TTA정보통신용어사전

한국정보통신기술협회(TTA)는 정보통신 기술 발전과 타 분야와의 기술 융합에 따라 무수히 생성되는 정보통신용어를 해설하고 표준화하여, 전문가뿐만 아니라 비전문가들도 올바르게 활용할 수

terms.tta.or.kr

E-UTRAN : Evolved Universal Terrestrial Radio Access Network

 

이미지 출처 : http://terms.tta.or.kr/dictionary/dictionaryView.do?word_seq=166867-11 

 

TTA정보통신용어사전

한국정보통신기술협회(TTA)는 정보통신 기술 발전과 타 분야와의 기술 융합에 따라 무수히 생성되는 정보통신용어를 해설하고 표준화하여, 전문가뿐만 아니라 비전문가들도 올바르게 활용할 수

terms.tta.or.kr

 

GTP : GPRS Tunneling Protocol -> GPRS 를 전달하는 IP 기반 통신 프로토콜 그룹

GPRS (General Packet Radio Service) 

Tunneling : 터널을 파고 나가는

 

GTP-C :  GTP Control

 

GTP-U : GTP User

 

HSS : Home Subscriber Server

사용자 프로파일을 갖는 중앙 DB로 MME에게 사용자 인증 정보와 사용자 프로파일을 제공한다.

 

HomeSubscriberServer(HSS)는 IPMultimediaSubsystem(IMS)내에서 사용된 주 가입자 데이터베이스이며 네트워크 내 다른 엔터티에 대한 가입자의 세부 정보를 제공합니다. IMS를 통해 사용자는 상태에 따라 다른 서비스에 대한 액세스를 허용하거나 거부할 수 있습니다.

 

IP : Internet Protocol

 

LTE : Long Term Evolution

HSDPA 보다 한층 진화된 휴대전화 고속 무선 데이터 패킷통신규격이다. HSDPA의 진화된 규격인 HSPA+와 함께 3.9세대 무선통신규격으로 불린다.

 

MAC : Medium Access Control

 

MME : Mobility Management Entity

- E-UTRAN 제어 평면 엔터티로, 사용자 인증과 사용자 프로파일 다운로드를 위하여 HSS와 통신하고, NAS 시그널링을 통해 UE에게 EPS 이동성 관리(EMM) 및 EPS Session 관리(ESM)기능을 제공한다.  

- LTE 망의 "두뇌" 역할을 하는 장비 그 역할은 UE를 인증(Authentication) ,  EPS 베어러를 관리 , 가입자의 Mobility 상태를 관리

 

 

NAS : Non Access Stratum

NAS는 UMTS 프로토콜 스택에서 UE와 코어 네트워크간의 시그널링, 트래픽 메시지를 주고 받기 위한 기능적인 계층이다. NAS 영역의 하부에는 Mobile Management영역이, 상부에는 Communication Management영역이 있다.

from 위키백과

 

NRM : Network Reference Model

각 형태의 통신망들이 네트워크 구조 각 구성요소별로 쉬운 참조 및 이해를 돕기위해, 해당 네트워크를 기능적으로 구분시킨 기능 블럭 다이어그램을 말함

http://www.ktword.co.kr/abbr_view.php?nav=2&m_temp1=3976&id=290

 

네트워크 참조모델 [정보통신기술용어해설]

 

www.ktword.co.kr

 

LTE 네트워크 참조 모델

이미지 출처 :  https://www.netmanias.com/ko/?m=view&id=techdocs&no=10634

 

LTE 네트워크 구조

 

www.netmanias.com

 

 

 

S-GW(Serving Gateway) : E-UTRAN과 EPC의 종단점이 된다. eNB 간 핸드오버 및 3GPP 시스템 간 핸드오버시 anchoring point가 된다. 

 

P-GW(Packet Data Network Gateway) : UE를 외부 PDN망과 연결해주며 패킷 filtering을 제공한다. UE에게 IP 주소를 할당하고 3GPP와 non-3GPP 간 핸드 오버시 mobility anchoring point로 동작한다. PCRF로부터 PCC 규칙을 수신하여 적용하며 (Policy Enforcement) UE 당 과금 기능을 제공한다. 주요 기능은 다음과 같다.

- IP 라우팅 및 fowarding

- Per-SDF / Per-User 기반 패킷 filtering 

- UE IP 주소 할당

- 3GPP와 non-3GPP 간 Mobility anchoring

- PCEF 기능

- Per-SDF/Per-User 과금

 

 

참조점 프로토콜 설명
LTE-Uu E-UTRA
(제어 평면, 사용자 평면)
UE와 eNB 간 무선 인터페이스로 제어 평면 및 사용자 평면을 정의한다.
X2 X2-AP (제어 평면)
GTP-U (사용자 평면)
두 eNB 간 인터페이스로 제어 평면 및 사용자 평면을 정의한다.
S1-U GTP-U eNB 와 S-GW 간 인터페이스로 사용자 평면을 정의한다. 베어러 당 GTP 터널링을 제공한다.
S1-MME S1-AP eNB와 MME 간 인터페이스로 제어 평면을 정의한다. 
S11 GTP-C MME와 S-GW 간 인터페이스로 제어 평면을 정의한다. 사용자 당 GTP 터널링을 제공한다.
S5 GTP-C(제어 평면)
GTP-U(사용자 평면)
S-GW와 P-GW간 인터페이스로 제어 평면 및 사용자 평면을 정의한다. 사용자 평면에서 베어러 당 GTP 터널링을 제공하고 제어 평면에서 사용자 당 GTP 터널 관리를 제공한다. 
S6a Diameter HSS와 MME 간 인터페이스로 제어 평면을 정의한다. UE 가입 정보 및 인증 정보를 교환한다.
Sp Diameter SPR과 PCRF 간 인터페이스로 제어 평면을 정의한다.
Gx Diameter PCRF와 P-GW간 인터페이스로 제어 평면을 정의한다. QoS 정책 및 과금 제어를 위한 인터페이스로 정책 제어 규칙 및 과금 규칙을 전달한다.
Gy Diameter OCS와 P-GW 간 인터페이스로 제어 평면을 정의한다. 
Gz GTP' OFCS와 P-GW간 인터페이스로 제어 평면을 정의한다.
SGi IP P-GW와 PDN 간 인터페이스로 사용자 평면과 제어 평면을 정의한다.
사용자 평면에서는 IETF 기반 IP 패킷 forwarding 프로토콜이 사용되고 제어 평면에서는 DHCP와 RADIUS/Diameter와 같은 프로토콜이 사용된다.

 

핸드오버 : 핸드오버는 단말기가 연결된 기지국의 서비스 공간에서 다른 기지국의 서비스 공간으로 이동할 때, 단말기가 다른 기지국의 서비스 공간에 할당한 통화 채널에 동조하여 서비스가 연결되는 기능을 일컫는다. from 위키백과 

 

OCS : Online Charging System

- 실시간 credit 제어를 제공하고 volume, time, event 기반 과금 기능을 제공한다. 

- 온라인 과금 시스템 은 통신 서비스 공급자가 고객의 실시간 서비스 사용기반 과금을 할 수 있도록 하는 시스템이다.

from 위키백과

 

OFCS : Offline Charging System

- CDR 기반 과금 정보를 제공한다.

- OFCS 는 오프라인 거래 시스템의 핵심 요소이다. OFCS 는 MSC, SGSN, CSCF등 잠재적으로 거래 관련 데이터를 제공하는 인터넷 노드들과 상호작용한다.

 

OSS : Operations Support System

운용 지원 시스템은 통신 서비스 제공자가 자사의 네트워크를 관리하기 위해 사용하는 컴퓨터 시스템이다. 네트워크 재고, 서비스 프로비저닝, 네트워크 구성, 장애 관리 등의 관리 기능을 지원한다. 사업 지원 시스템과 더불어 다양한 단대단 통신 서비스들을 지원하기 위해 사용된다. from 위키백과

 

PCC : Policy and Charging Control

정책 및 과금 제어

 

PCEF : Policy and Charging Enforcement Function

PCEF는 정책 및 청구 기능에서 수신한 결정을 시행하고 PCRF에 백 엑세스 및 및 가입자 정보를 중계하도록 설계 되었다.

 

PCRF : Policy and Charging Rule Function

정책 및 과금 제어 엔터티로 정책 제어 결정과 과금 제어 기능을 제공한다. PCRF에서 생성된 PCC 규칙은 P-GW로 전달된다.

 

정책 및 청구 규칙 기능은 멀티미디어 네트워크에서 정책 규칙을 결정하기 위해 실시간으로 지정된 소프트웨어 노드입니다. 정책 도구로서 PCRF는 차세대 네트워크에서 중심적인 역할을합니다. from 위키백과

 

PDCP : Packet Data Convergence Protocol

PDCP는 UMTS내 무선 트래픽 스택의 계층 중 하나이며, IP 헤더 압축 및 압축 해지, 사용자 데이터의 전송, Radio Bearer에 대한 시퀀스 번호 유지를 수행한다. 압축 기술은 RFC 2507 또는 RFC 3095에 기반한다.

Convergence : 수렴

 

PDN : Packet Data Network

공공 데이터 네트워크는 일반 대중을위한 데이터 전송 서비스를 제공하기 위해 통신 관리 부서 또는 공인 된 개인 운영 기관에서 설정하고 운영하는 네트워크입니다.

 

QoS : Quality of Service

QoS는 다른 응용 프로그램, 사용자, 데이터 흐름 등에 우선 순위를 정하여, 데이터 전송에 특정 수준의 성능을 보장하기 위한 능력을 말한다.

 

RLC : Radio Link Control

무선 링크 제어는 공중 인터페이스에서 UMTS 및 LTE에 사용되는 계층 2 무선 링크 프로토콜입니다. 이 프로토콜은 UMTS의 TS 25.322, LTE의 TS 36.322 및 5G New Radio의 TS 38.322에서 3GPP로 지정됩니다. from 위키백과

 

RRC : Radio Resource Control

무선 자원 제어 프로토콜은 무선 인터페이스의 UMTS 및 LTE에서 사용됩니다. UE와 eNB 사이에 존재하며 IP 레벨에 존재하는 계층입니다. 이 프로토콜은 UMTS의 경우 TS 25.331 및 LTE의 경우 TS 36.331에서 3GPP로 지정됩니다.

 

RRM : Radio Resource Management

무선 자원 제어 프로토콜은 무선 인터페이스의 UMTS 및 LTE에서 사용됩니다. UE와 eNB 사이에 존재하며 IP 레벨에 존재하는 계층입니다. 이 프로토콜은 UMTS의 경우 TS 25.331 및 LTE의 경우 TS 36.331에서 3GPP로 지정됩니다. 위키백과

 

S1AP : S1 Application Protocol

S1애플리케이션 프로토콜(S1AP)은 E-UTRAN과 진화된 패킷 코어(EPC)사이에서 제어부 신호 전달을 제공합니다.

 

SCTP : Stream Control Transmission Protocol

스트림 제어 전송 프로토콜은 컴퓨터 네트워킹에서 프로토콜 번호 132를 사용하는 전송 계층 프로토콜의 하나로서, 잘 알려진 프로토콜인 전송 제어 프로토콜, 사용자 데이터그램 프로토콜와 비슷한 역할을 수행한다. TCP와 UDP의 동일한 서비스 기능들 가운데 일부를 제공한다. 

 

SDF : Service Data Flow

통화와 관련된 음성 패킷의 흐름이나 웹 사이트에서의 스트리밍 데이터와 같은 가입자에게 제공되는 서비스를 나타내는 패킷의 흐름을 설명합니다.

 

SN : Sequence Number

시퀸스 넘버

 

SPR : Subscriber Profile Repository

- PCR에게 가입자 및 가입관련 정보를 제공한다. PCRF는 이를 수신하여 가입자 기반 정책을 수행하고 과금 규칙을 생성한다. 

- 3GPP 표준에 따라 정의된 가입자별 정책 제어 데이터를 저장 및 관리하는 시스템이다.

 

TEID : Tunnel Endpoint Identifier

TEID는 수신 GTP-U(GPRS터널링 프로토콜-사용자)또는 GTP-C(GPRS터널링 프로토콜-제어)프로토콜 실체에서 터널 끝점을 명확하게 식별합니다. GTP터널의 수신 측은 전송 측에서 사용할 TEID값을 로컬로 할당합니다. TEID값은 GTP-C메시지(또는 IMT-2000지상파 무선 액세스 네트워크의 RANAP)를 사용하여 터널 엔드 포인트 간에 교환된다.

 

UE : User Equipment

- LTE-Uu 무선 인터페이스를 통하여 eNB와 접속한다.

- User Equipment는 정보통신 용어의 하나로 5G 서비스가 제공되는 통신대상 단말의 수를 의미한다. UE Speed는 서비스가 제공되는 통신 대상 지역에 위치한 단말의 이동속도를 의미한다. from 위키백과

 

eNB Evolved Node B

사용자에게 무선 인터페이스를 제공한다. 무선 베어러 제어, 무선 수락 제어, 동적 무선 자원 할당, load balancing 및 셀 간 간섭제어(ICIC)와 같은 무선 자원 관리(RRM) 기능을 제공한다. 

evolved : 진화된

 

UDP : User Datagram Protocol

transport 계층의 통신 프로토콜로, 신뢰성이 낮은 프로토콜로써, 완전성을 보증하지 않으나, 가상회선을 굳이 확립할 필요가 없고, 유연하며 효율적 응용의 데이터 전송에 적합

 

X2-AP : X2 Application Protocol 

X2AP프로토콜은 E-UTRAN내의 UE이동성을 처리하는 데 사용

 

3GPP는 이동통신 관련 단체들 간의 공동 연구 프로젝트로 국제전기통신연합의 IMT-2000 프로젝트의 범위 내에서 - 전 세계적으로 적용 가능한 - 3세대 이동통신 시스템 규격의 작성을 목적으로 하고 있다.

 

Diameter

다이어미터 프로토콜은 컴퓨터 네트워크에서 사용되는 인증, 인가 및 과금 프로토콜이다. 다이어미터 프로토콜 보다 앞서 사용된 RADIUS 프로토콜에서 훨씬 더 유용하게 진화되었고 RADIUS 프로토콜을 대체하고 있다. from 위키백과

 

 

 

 

+ Recent posts