선형 검색

(Liner Search) : lsearch(3), lfind(3)

이진 검색

(Binary Search) : qsort(3), bsearch(3)

해시 검색

(hash search) : hcreate(3), hsearch(3), hdestroy(3)

트리 검색

(tree search) : tsearch(3), tfind(3), twalk(3), tdelete(3)

Linking is the final stage of the gcc compilation process.

In the linking process, object files are linked together and all the references to external symbols are resolved, final addresses are assigned to function calls, etc.

In this article we will mainly focus on the following aspects of gcc linking process:

  1. Object files and how are they linked together
  2. Code relocations


Before you read this article, make sure you understand all the 4 stages that a C program has to go through before becoming an executable (pre-processing, compilation, assembly and linking).

LINKING OBJECT FILES

Lets understand this first step through an example. First create the following main.c program.

$ vi main.c
#include <stdio.h> 

extern void func(void); 

int main(void)
{
    printf("\n Inside main()\n");
    func(); 

    return 0;
}

Next create the following func.c program. In the file main.c we have declared a function func() through keyword ‘extern’ and have defined this function in a separate file func.c

$ vi func.c
void func(void)
{
    printf("\n Inside func()\n");
}

Create the object file for func.c as shown below. This will create the file func.o in the current directory.

$ gcc -c func.c

Similarly create the object file for main.c as shown below. This will create the file main.o in the current directory.

$ gcc -c main.c

Now execute the following command to link these two object files to produce a final executable. This will create the file ‘main’ in the current directory.

$ gcc func.o main.o -o main

When you execute this ‘main’ program you’ll see the following output.

$ ./main
Inside main()
Inside func()

From the above output, it is clear that we were able to link the two object files successfully into a final executable.

What did we acheive when we separated function func() from main.c and wrote it in func.c?

The answer is that here it may not have mattered much if we would have written the function func() in the same file too but think of very large programs where we might have thousands of lines of code. A change to one line of code could result in recompilation of the whole source code which is not accceptable in most cases. So, very large programs are sometimes divided into small peices which are finaly linked together to produce the executable.

The make utility which works on makefiles comes into the play in most of these situations because this utility knows which source files have been changed and which object files need to be recompiled. The object files whose corresponding source files have not been altered are linked as it is. This makes the compilation process very easy and manageable.

So, now we understand that when we link the two object files func.o and main.o, the gcc linker is able to resolve the function call to func() and when the final executable main is executed, we see the printf() inside the function func() being executed.

Where did the linker find the definition of the function printf()? Since Linker did not give any error that surely means that linker found the definition of printf(). printf() is a function which is declared in stdio.h and defined as a part of standard ‘C’ shared library (libc.so)

We did not link this shared object file to our program. So, how did this work? Use the ldd tool to find out, which prints the shared libraries required by each program or shared library specified on the command line.

Execute ldd on the ‘main’ executable, which will display the following output.

$ ldd main
linux-vdso.so.1 =>  (0x00007fff1c1ff000)
libc.so.6 => /lib/libc.so.6 (0x00007f32fa6ad000)
/lib64/ld-linux-x86-64.so.2 (0x00007f32faa4f000)

The above output indicates that the main executable depends on three libraries. The second line in the above output is ‘libc.so.6′ (standard ‘C” library). This is how gcc linker is able to resolve the function call to printf().

The first library is required for making system calls while the third shared library is the one which loads all the other shared libraries required by the executable. This library will be present for every executable which depends on any other shared libraries for its execution.

During linking, the command that is internally used by gcc is very long but from users prespective, we just have to write.

$ gcc <object files> -o <output file name>

CODE RELOCATION

Relocations are entries within a binary that are left to be filled at link time or run time. A typical relocation entry says: Find the value of ‘z’ and put that value into the final executable at offset ‘x’

Create the following reloc.c for this example.

$ vi reloc.c
extern void func(void); 

void func1(void)
{
    func();
}

In the above reloc.c we declared a function func() whose definition is still not provided, but we are calling that function in func1().

Create an object file reloc.o from reloc.c as shown below.

$ gcc -c reloc.c -o reloc.o

Use readelf utility to see the relocations in this object file as shown below.

$ readelf --relocs reloc.o
Relocation section '.rela.text' at offset 0x510 contains 1 entries:
Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000000005  000900000002 R_X86_64_PC32     0000000000000000 func - 4
...

The address of func() is not known at the time we make reloc.o so the compiler leaves a relocation of type R_X86_64_PC32. This relocation indirectly says that “fill the address of the function func() in the final executable at offset 000000000005”.

The above relocation was corresponding to the .text section in the object file reloc.o (again one needs to understand the structure of ELF files to understand various sections) so lets disassemble the .text section using objdump utility:

$ objdump --disassemble reloc.o
reloc.o:     file format elf64-x86-64 

Disassembly of section .text: 

0000000000000000 <func1>:
   0:	55                   	push   %rbp
   1:	48 89 e5             	mov    %rsp,%rbp
   4:	e8 00 00 00 00       	callq  9 <func1+0x9>
   9:	c9                   	leaveq
   a:	c3                   	retq

In the above output, the offset ’5′ (entry with value ’4′ relative to starting address 0000000000000000) has 4 bytes waiting to be writen with the address of function func().

So, there is a relocation pending for the function func() which will get resolved when we link reloc.o with the object file or library that contains the defination of function func().

Lets try and see whether this relocation gets reolved or not. Here is another file main.c that provides defination of func() :

$ vi main.c
#include<stdio.h> 

void func(void) // Provides the defination
{
    printf("\n Inside func()\n");
} 

int main(void)
{
    printf("\n Inside main()\n");
    func1();
    return 0;
}

Create main.o object file from main.c as shown below.

$ gcc -c main.c -o main.o

Link reloc.o with main.o and try to produce an executable as shown below.

$ gcc reloc.o main.o -o reloc

Execute objdump again and see whether the relocation has been resolved or not:

$ objdump --disassemble reloc > output.txt

We redirected the output because an executable contains lots and lots of information and we do not want to get lost on stdout.
View the content of the output.txt file.

$ vi output.txt
...
0000000000400524 <func1>:
400524:       55                      push   %rbp
400525:       48 89 e5                mov    %rsp,%rbp
400528:       e8 03 00 00 00          callq  400530 <func>
40052d:       c9                      leaveq
40052e:       c3                      retq
40052f:       90                      nop
...

In the 4th line, we can clearly see that the empty address bytes that we saw earlier are now filled with the address of function func().

To conclude, gcc compiler linking is such a vast sea to dive in that it cannot be covered in one article. Still, this article made an attempt to peel off the first layer of linking process to give you an idea about what happens beneath the gcc command that promises to link different object files to produce an executable.

'Linux > C/C++' 카테고리의 다른 글

memory lock  (0) 2012.07.16
C에서 제공하는 메모리 정렬 및 검색 함수들  (0) 2012.07.16
리눅스 명령어 : readelf  (0) 2012.07.16
variable length array  (0) 2012.07.16
nslookup -> getaddrinfo 사용 사례  (0) 2012.07.16

ELF 는 실행 가능한 바이너리 또는 오브젝트 파일 등의 형식을 규정한 것입니다. ELF 파일은 ELF 헤더가 맨 앞에 위치하고, 프로그램 헤더 테이블과 섹션 헤더 테이블이 그 뒤에 위치한다. (사실 더 복잡한 구조지만 큰 구조가 그렇다는 이야기)

ELF 헤더는 파일이 ELF 포맷일 경우 파일 맨앞에 반드시 존재하며, readelf -h 명령어로 elf의 헤더를 볼 수 있습니다. 명령어 참~ 쉽죠? read elf 입니다. 기억하기 쉬워요. 파일 분석시 굉장히 많이 사용하는 명령어 입니다.

사용자 삽입 이미지

Entry point Address - 시작하는 가상주소를 나타낸다.



프로그램 헤더는 readelf -l '파일명' 으로 섹션 헤더는 readelf -S '파일명'으로 확인 가능하다.

사용자 삽입 이미지

shstrtab


위 그림에서 빨간색 네모로 쳐진 shstrtab 의 type 항목을 보면 String table 이라는 것을 알 수 있다. base address 는 0x00ff0c 크기는 0x0000c7 인 것을 알 수 있다. od --skip-bytes 0x00ff0c --read-bytes 0xc7 -t x1z -A x zip 이라고 입력하면 shstrtab 의 내용을 볼 수 있다.

심볼 테이블은 readelf -s 파일명으로 볼 수 있다.

배운 명령어
  • readelf -h (ELF 헤더 확인) 
  • readelf -l (Program 헤더 확인) 
  • readelf -S (Section 헤더 확인)
  • readelf -s (심볼 테이블 확인)
  • od --skip-bytes 0x00ff0c --read-bytes 0xc7 -t x1z -A x zip


variable length array Linux/C/C++ 2012. 7. 16. 10:08

C에서는 가변길이의 배열을 잡을 수 없다는 사실이 아주 오래전 부터 머리속에 각인되어 있었다.

즉,

void makeArr(int number)
{
int arr[number];
....
....
}

위와 같은 형태로는 배열을 생성할 수 없다.
배열의 길이를 런타임에 결정하기 위해서는 malloc() 함수를 사용해야 한다.

int* arr = (int)malloc(sizeof(int)*number);

위오 같은 형태로 배열을 생성해 줘야 런타임에 배열의 길이를 정하여 생성할 수 있었다.


근데!!! 이게 바꼈단다.
언제부터인지는 모르지만 C99의 표준을 반영한 컴파일러는 Variable Length Array 를 지원한단다.
즉,

int arr[number];

와 같은 형태로 선언하여 컴파일 해도 전혀 문제가 되지 않는다는 얘기다.
확인하기 위해 현재 내 컴퓨터에 깔려 있는 GCC 4.4.1 버전으로 테스해 봤더니 정상적으로 동작한다.
C99이 99년도에 나왔다는데 평소 C를 전혀 사용하지 않으니.. 관심도 없었나 보다.

참고로 MS계열의 컴파일러는 이를 지원하지 않는다. Visual Studio 2008 에서 테스트 해 봤지만 컴파일 에러를 뿜었다.


컴파일 옵션 : --std=c99

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
 
 
int main(int argc, char **argv)
{
        struct addrinfo hints;
        struct addrinfo *result, *rp;
        struct sockaddr_in *sin;
        struct sockaddr_in6 *sin6;
        int *listen_fd;
        int listen_fd_num=0;
 
        char buf[80] = {0x00,};
        int i = 0;
       /* if(argc != 2)
        {
                printf("Usage : %s [port]\n", argv[0]);
                return 1;
        }
     */

        memset(&hints, 0x00, sizeof(struct addrinfo));
 
        hints.ai_flags = AI_PASSIVE;
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;
 
        if(getaddrinfo(argv[1], NULL, &hints, &result) != 0 )
        {
                perror("getaddrinfo");
                return 1;
        }
 
        for(rp = result ; rp != NULL; rp = rp->ai_next)
        {
                listen_fd_num++;
        }
        listen_fd = malloc(sizeof(int)*listen_fd_num);
 
        printf("Num %d\n", listen_fd_num);
        for(rp = result, i=0 ; rp != NULL; rp = rp->ai_next, i++)
        {
                if(rp->ai_family == AF_INET)
                {
                        sin = (void *)rp->ai_addr;
                        inet_ntop(rp->ai_family, &sin->sin_addr, buf, sizeof(buf));
                        printf("<bind 정보 %d %d %s>\n", rp->ai_protocol, rp->ai_socktype, buf);
                }
                else if(rp->ai_family == AF_INET6)
                {
                        sin6 = (void *)rp->ai_addr;
                        inet_ntop(rp->ai_family, &sin6->sin6_addr, buf, sizeof(buf));
                        printf("<bind 정보 %d %d %s>\n", rp->ai_protocol, rp->ai_socktype, buf);
                }
                if((listen_fd[i] = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0)
                {
                        printf("Socket Create Error\n");
                }
                if(rp->ai_family == AF_INET6)
                {
                        int opt = 1;
                        setsockopt(listen_fd[i], IPPROTO_IPV6, IPV6_V6ONLY, (char *)&opt, sizeof(opt));
                }
 
                if(bind(listen_fd[i], rp->ai_addr, rp->ai_addrlen) != 0)
                {
                        if(errno != EADDRINUSE);
                        {
                                perror("bind error\n");
                                return 1;
                        }
                }
                if(listen(listen_fd[i], 5) != 0)
                {
                        perror("listen error\n");
                        return 1;
                }
        }
        freeaddrinfo(result);
        pause();
        return 1;
}

'Linux > C/C++' 카테고리의 다른 글

리눅스 명령어 : readelf  (0) 2012.07.16
variable length array  (0) 2012.07.16
LFS (Large File Support)  (0) 2012.07.11
C 언어 레퍼런스 - setvbuf 함수  (0) 2012.07.11
recvmsg sendmsg example from italy  (0) 2012.07.11

리눅스에서 LFS(Large File Support) 를 구현하는 방법은 두가지가 있습니다.
1) 32bit 와 64bit 파일관련 함수를 나눠서 쓰는 경우

#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE

위의 두가지를 define 해주고 사용합니다. 그러면 open(), read(), write() ... 는 32bit 함수로
open64(), read64(), write64() ... 는 64bit 함수로 사용됩니다.
64bit 함수들은 64bit 형식의 off64_t 형을 오프셋으로 사용합니다.
그리고 64bit 형의 파일 탐색은 ftello(), fseeko() 함수를 대신 사용합니다.

ps : off_t -> of64_t

open() -> opne64() .....

2) 기존의 32bit 함수를 모두 64bit 로 전환
#define _LARGEFILE_SOURCE
#define _FILE_OFFSET_BITS 64

위의 두가지를 define 하면 open(), read(), write() 등이 모두 64bit 형으로 작동합니다.
따라서 off_t 를 사용해도 자동적으로 off64_t 로 전환됩니다.
마찬가지로 64bit 형의 파일 탐색은 ftello(), fseeko() 함수를 사용합니다.


http://www.suse.de/~aj/linux_lfs.html

'Linux > C/C++' 카테고리의 다른 글

variable length array  (0) 2012.07.16
nslookup -> getaddrinfo 사용 사례  (0) 2012.07.16
C 언어 레퍼런스 - setvbuf 함수  (0) 2012.07.11
recvmsg sendmsg example from italy  (0) 2012.07.11
Socket Programming (sendmsg(), recvmsg() example)  (0) 2012.07.11
#include <stdio.h> // C++ 에서는 <cstdio>

int setvbuf ( FILE * stream, char * buffer, int mode, size_t size );

스트림 버퍼링 방식을 변경한다.
버퍼를 특정한 스트림의 입출력 연산에 사용될 수 있도록 변경한다. 이 함수는 버퍼링 방식과 버퍼의 크기를 설정할 수 있게 지원한다.
버퍼의 크기는 세번째 인자에 바이트 수로 전달된다.
만일 버퍼를 설정하지 않는다면 (즉, 두 번째 인자가 NULL 이라면), 시스템은 동적으로, 함수에 의해 요청된 크기 만큼 메모리를 할당하게 되며 이를 스트림의 버퍼로 사용하게 된다.
mode 인자는 이 버퍼를 fully buffered, line buffered, 아니면 unbuffered 로 할 지 결정한다.

fully buffered 스트림의 경우 읽기 작업은 스트림에 대응되는 장비에 바로 쓰여지지 않는다. 그 대신에 쓰여질 데이터는 버퍼에 잠시 저장되었다가 버퍼에 일정한 블록 이상 쌓이게 되면 그제서야 장비에 쓰여지게 된다. 물론, 한 블록이 다 채워지지 않았는데도 불구하고 스트림을 강제로 비움으로써 (flush) 장비에 쓸 수 있는데 이는 fflush 함수를 호출하거나 fclose 함수를 호출해 파일을 닫으면 된다. 참고적으로 모든 버퍼는 프로그램이 종료시 자동적으로 비워지게 된다.

line buffered 스트림의 경우 버퍼에 개행 문자가 입력될 때 마다 장비에 쓰여지게 된다.

unbuffered 스트림의 경우 데이터는 버퍼와 같은 중간 경유지를 거치지 않고 직접적으로 쓰여지게 된다. 즉, 쓰기 작업을 할 때 바로바로 쓰이게 된다.

모든 열려진 파일들은 기본적으로 할당된 버퍼를 가지고 있다. 이 함수는 그렇게 할당된 버퍼의 크기를 재조정 하거나, 사용자가 특별히 할당한 버퍼로 버퍼를 바꾸거나, 파일의 버퍼링 방식을 변경할 수 있게 해준다. 표준 출력(stdout) 이나 표준 오류(stderr) 같은 시스템 표준 스트림은 리다이렉트(redirect) 되지 않는 한 기본적으로 unbuffered 로 설정되어 있다.

   인자
 

stream

작업을 수행할 열린 스트림의 FILE 객체를 가리키는 포인터.

buffer

유저가 할당한 버퍼로 최소한 1 바이트의 크기는 되야 한다.
만일 NULL 로 설정하면 함수는 자동으로 지정한 크기의 버퍼를 할당하게 된다

mode

파일 버퍼링의 형식을 지정한다.
_IOFBF 완전한 버퍼링(Full Buffering): 앞에서 설명한 fully buffered 스트림을 일컫는다.
_IOLBF 행 버퍼링(Line Buffering): 출력시, 버퍼가 채워지거나 스트림에 개행 문자가 입력되었다면 데이터가 버퍼에서 출력된다. 입력 시에는 버퍼가 개행 문자를 만날 때 까지 버퍼를 채우게 된다.
_IONBF 버퍼링 사용 안함(No Buffering): 버퍼를 사용하지 않는다. 각각의 입출력 작업은 버퍼를 지나지 않고 요청 즉시 진행되며 이 겨우 buffer 인자와 size 인자는 모두 무시된다.

size


지정할 버퍼의 크기로 단위는 바이트 이다. 이 때, 버퍼로 지정한 배열의 크기 보다 반드시 작거나 같아야 한다.
만일 이 인자가 NULL 이라면 컴퓨터가 스스로 버퍼가 취할 수 있는 최소의 크기를 결정하게 된다.

   리턴값
 

만일 버퍼가 성공적으로 지정 되었다면 0 이 리턴된다.
그렇지 않을 경우 0 이 아닌 값이 리턴되는데 보통 적당하지 않은 size 값을 인자로 넘겼거나, size 에 NULL 을 넘겼지만 컴퓨터가 메모리를 할당하는 과정 중 오류가 발생하는 경우이다.

   실행 예제
 

/*

파일을 _IOFBF 형식으로 열며, 버퍼는 크기가 1024 바이트로 컴퓨터가 자동으로 할당하게 한다.
이 예제는 http://www.cplusplus.com/reference/clibrary/cstdio/setvbuf/
에서 가져왔습니다.

 */
#include <stdio.h>
int main ()
{
    FILE *pFile;
    pFile=fopen ("myfile.txt","w");
    setvbuf ( pFile , NULL , _IOFBF , 1024 );

    /* 여러 파일 입출력 작업들 */

    fclose (pFile);

    return 0;
}


스트림 버퍼 형식이 _IOFBF, 즉 fully buffered 스트림이기 때문에 버퍼가 다 차지 않는 이상 쓰기 작업을 하지 않게 된다. 즉, 버퍼에 1024 바이트 이상 쓰이지 않는 이상 myfile.txt 에 쓰이지 않는다.


'Linux > C/C++' 카테고리의 다른 글

nslookup -> getaddrinfo 사용 사례  (0) 2012.07.16
LFS (Large File Support)  (0) 2012.07.11
recvmsg sendmsg example from italy  (0) 2012.07.11
Socket Programming (sendmsg(), recvmsg() example)  (0) 2012.07.11
sendmsg(2) - Linux man page  (0) 2012.07.11

http://www.sikurezza.org/devel/msg00122.html


Archivio: devel@sikurezza.org
Soggetto: Re: recvmsg()
Mittente: Mauro Tortonesi
Data: 19 May 2002 20:49:01 -0000
On Fri, 17 May 2002, Kundera wrote:

> Ciao a tutti, vorrei sapere sta cosa, utilizzando un 
> sok=socket(AF_INET6,SOCK_RAW,IPPROTO_ICMPV6) e la n=recvmsg(sok,&msg,0) 
> l'indirizzo ipv6 del pacchetto in arrivo dove viene messo!?


dalla manpage di recvmsg:

The recvmsg call uses a msghdr structure to minimize the number of 
directly supplied parameters.  This structure has the following form, 
as defined in <sys/socket.h>:

              struct msghdr {
                  void         * msg_name;     /* optional address */
                  socklen_t    msg_namelen;    /* size of address */
                  struct iovec * msg_iov;      /* scatter/gather array */
                  size_t       msg_iovlen;     /* # elements in msg_iov */
                  void         * msg_control;  /* ancillary data, see below */
                  socklen_t    msg_controllen; /* ancillary data buffer len */
                  int          msg_flags;      /* flags on received message */
              };

Here msg_name and msg_namelen specify the source  address  if  the socket  
is  unconnected; msg_name  may  be  given  as a null pointer if no names are 
desired or required.  The fields msg_iov and msg_iovlen describe scatter-gather 
locations, as discussed  in  readv(2). The field  msg_control,  which  has length 
msg_controllen, points to a buffer for other protocol control related messages or 
miscellaneous ancillary data. When recvmsg is  called,  msg_controllen should 
contain the length of the available buffer in msg_control; upon return from a
successful call it will contain the length of the control message sequence.


quindi basta fare:


/* occhio ai jumbo datagrams!!! non e' detto che un buffer di 64kb sia 
 * sufficiente */
const size_t BUFFER_LENGHT = 65536;

int ret;
uint8_t *my_buffer;
struct msghdr msg;
struct iovec iov;
struct sockaddr_storage ss;
struct sockaddr_in6 *sin6;

memset(&msg, 0, sizeof(msg));

my_buffer = (uint8_t *)malloc(BUFFER_LENGTH);
if (my_buffer == NULL) exit(EXIT_FAILURE);

/* qui setti correttamente il buffer di destinazione */
iov.iov_base = my_buffer;
iov.iov_len = BUFFER_LENGHT;

msg.msg_name = &ss;
msg.msg_namelen = sizeof(ss);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;

ret = recvmsg(sok, &msg, 0);

/* qui controlli che non ci siano stati errori */
...

sin6 = (struct sockaddr_in6 *)&ss;


e a questo punto ti ritrovi nella struttura sin6 (e piu' precisamente nel 
campo sin6_addr) l'indirizzo ipv6 del mittente. comunque se non vuoi 
allocare un buffer di 128 byte (sizeof(struct sockaddr_storage) ritorna 
128 bytes) solo per ottenere l'indirizzo ipv6 del mittente puoi procedere 
alla estrapolazione dell'indirizzo ipv6 del mittente utilizzando un
altro metodo (molto piu' complesso) previsto dallo rfc2292, che fa uso 
degli "ancillary data objects". vedi piu' sotto.


> ho anche a disposizione l'header ipv6 in arrivo da qualche parte? 

no, la funzione recvmsg _NON_ ti restituisce l'intero pacchetto ipv6
(al contrario di ipv4). dal draft-ietf-ipngwg-rfc2292bis-07.txt (che 
e' destinato a sostituire lo rfc2292) sezione 3, pagina 19:


3.  IPv6 Raw Sockets

   [...]

   Another difference from IPv4 raw sockets is that complete packets
   (that is, IPv6 packets with extension headers) cannot be sent or
   received using the IPv6 raw sockets API.  Instead, ancillary data
   objects are used to transfer the extension headers and hoplimit
   information, as described in Section 6.  Should an application need
   access to the complete IPv6 packet, some other technique, such as the
   datalink interfaces BPF or DLPI, must be used.


pertanto la situazione si complica enormemente. innanzi tutto devi 
dire alla socket di fornirti i dati sul mittente:

int on = 1;
setsockopt(sok, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));


poi devi preparare un buffer per gli ancillary data objects restituiti da 
recvmsg, e quando ricevi un pacchetto devi andare ad analizzare la struttura 
in6_pktinfo che ti viene restituita seguendo la seguente procedura:


/* 1kb e' anche troppo, ma meglio stare sul sicuro */
const size_t ADO_BUF_SIZE = 1024;
const size_t BUFFER_LENGHT = 65536;

int ret;
uint8_t *ado_buffer;
uint8_t *my_buffer;
struct msghdr msg;
struct iovec iov;
int found = 0;
struct in6_pktinfo info;
struct cmsghdr *cm;
struct in6_addr sender_address;

ado_buffer = (uint8_t *)malloc(ADO_BUF_SIZE);
if (ado_buffer == NULL) exit(EXIT_FAILURE);

my_buffer = (uint8_t *)malloc(BUFFER_LENGTH);
if (my_buffer == NULL) exit(EXIT_FAILURE);

/* qui setti correttamente il buffer di destinazione */
iov.iov_base = my_buffer;
iov.iov_len = BUFFER_LENGHT;

memset(&msg, 0, sizeof(msg));

msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = ado_buffer;
msg.msg_controlen = ADO_BUF_SIZE;

ret = recvmsg(sok, &msg, 0);

/* qui controlli che non ci siano stati errori */
...

for (cm = CMSG_FIRSTHDR(&msg); cm != NULL; cm = CMSG_NXTHDR(&msg, cm)) {
	if (cm->cmsg_level == IPPROTO_IPV6 &&
            cm->cmsg_type == IPV6_PKTINFO &&
            cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
		info = *(struct in6_pktinfo*)CMSG_DATA(cm);
		found = 1;
		break;
	}
}

sender_address = info.ipi6_addr;


spero di essere stato chiaro anche se sono almeno due anni che non uso
le raw socket.

comunque la tua domanda mi sembra un po' off-topic per questa lista. ti 
consiglio di dare un'occhiata al mio sito:

http://project6.ferrara.linux.it

che si occupa esclusivamente di ipv6 e di iscriverti a qualcuna delle 
nostre mailing list (ad esempio project6-it e projec6-devel). 



p.s. scusa la mia insana curiosita', ma che tipo di software stai 
     scrivendo? l'ultima volta che ho lavorato sulle raw socket icmpv6 e' 
     stato quando ho fatto il porting del ping di netkit-base ad ipv6 ;-)


-- 
Aequam memento rebus in arduis servare mentem...

Mauro Tortonesi			mauro@ferrara.linux.it
Ferrara Linux User Group	http://www.ferrara.linux.it
Project6 - IPv6 for Linux	http://project6.ferrara.linux.it


________________________________________________________
http://www.sikurezza.org - Italian Security Mailing List


'Linux > C/C++' 카테고리의 다른 글

LFS (Large File Support)  (0) 2012.07.11
C 언어 레퍼런스 - setvbuf 함수  (0) 2012.07.11
Socket Programming (sendmsg(), recvmsg() example)  (0) 2012.07.11
sendmsg(2) - Linux man page  (0) 2012.07.11
recvmsg(2) - Linux man page  (0) 2012.07.11
   struct msghdr        Msghdr;
   struct iovec         Iov;

   /* ...... */

    Iov.iov_base = (void *)msgcontent;
    Iov.iov_len = sizeof(msgcontent);

    Msghdr.msg_name = &their_addr;
    Msghdr.msg_namelen = sizeof(struct sockaddr_in);
    Msghdr.msg_iov = (struct iovec *) &Iov;
    Msghdr.msg_iovlen = 1;
    Msghdr.msg_control = NULL;
    Msghdr.msg_controllen = 0;
    
    numbytes = sendmsg(sockfd, &Msghdr, 0);



/////////////////////////////////////////////////////////////////////


 char    buf[MAXBUFLEN];
    struct sockaddr_in  their_addr; 
    struct msghdr       Msghdr;
    struct iovec        Iovec;

    Iovec.iov_base = (void *)buf;
    Iovec.iov_len = sizeof(buf);

    Msghdr.msg_name = &their_addr;
    Msghdr.msg_namelen = sizeof(struct sockaddr_in);
    Msghdr.msg_iov = &Iovec;
    Msghdr.msg_iovlen = 1;

    numbytes = recvmsg(sockfd, &Msghdr, 0);


Name

send, sendto, sendmsg - send a message on a socket

Synopsis

#include <sys/types.h>
#include <sys/socket.h>

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
               const struct sockaddr *dest_addr, socklen_t addrlen);

ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

Description

The system calls send(), sendto(), and sendmsg() are used to transmit a message to another socket.

The send() call may be used only when the socket is in a connected state (so that the intended recipient is known). The only difference between send() and write(2) is the presence of flags. With a zero flags argument, send() is equivalent to write(2). Also, the following call

send(sockfd, buf, len, flags);

is equivalent to

sendto(sockfd, buf, len, flags, NULL, 0);

The argument sockfd is the file descriptor of the sending socket.

If sendto() is used on a connection-mode (SOCK_STREAM, SOCK_SEQPACKET) socket, the arguments dest_addr and addrlen are ignored (and the error EISCONN may be returned when they are not NULL and 0), and the error ENOTCONN is returned when the socket was not actually connected. Otherwise, the address of the target is given by dest_addr with addrlen specifying its size. For sendmsg(), the address of the target is given by msg.msg_name, with msg.msg_namelen specifying its size.

For send() and sendto(), the message is found in buf and has length len. For sendmsg(), the message is pointed to by the elements of the array msg.msg_iov. The sendmsg() call also allows sending ancillary data (also known as control information).

If the message is too long to pass atomically through the underlying protocol, the error EMSGSIZE is returned, and the message is not transmitted.

No indication of failure to deliver is implicit in a send(). Locally detected errors are indicated by a return value of -1.

When the message does not fit into the send buffer of the socket, send() normally blocks, unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail with the error EAGAIN or EWOULDBLOCK in this case. The select(2) call may be used to determine when it is possible to send more data.

The flags argument is the bitwise OR of zero or more of the following flags.

MSG_CONFIRM (Since Linux 2.3.15)
Tell the link layer that forward progress happened: you got a successful reply from the other side. If the link layer doesn't get this it will regularly reprobe the neighbor (e.g., via a unicast ARP). Only valid on SOCK_DGRAM and SOCK_RAW sockets and currently only implemented for IPv4 and IPv6. See arp(7) for details.
MSG_DONTROUTE
Don't use a gateway to send out the packet, only send to hosts on directly connected networks. This is usually used only by diagnostic or routing programs. This is only defined for protocol families that route; packet sockets don't.
MSG_DONTWAIT (since Linux 2.2)
Enables nonblocking operation; if the operation would block, EAGAIN or EWOULDBLOCK is returned (this can also be enabled using the O_NONBLOCK flag with the F_SETFL fcntl(2)).
MSG_EOR (since Linux 2.2)
Terminates a record (when this notion is supported, as for sockets of type SOCK_SEQPACKET).
MSG_MORE (Since Linux 2.4.4)
The caller has more data to send. This flag is used with TCP sockets to obtain the same effect as the TCP_CORK socket option (see tcp(7)), with the difference that this flag can be set on a per-call basis.

Since Linux 2.6, this flag is also supported for UDP sockets, and informs the kernel to package all of the data sent in calls with this flag set into a single datagram which is only transmitted when a call is performed that does not specify this flag. (See also the UDP_CORK socket option described in udp(7).)

MSG_NOSIGNAL (since Linux 2.2)
Requests not to send SIGPIPE on errors on stream oriented sockets when the other end breaks the connection. The EPIPE error is still returned.
MSG_OOB
Sends out-of-band data on sockets that support this notion (e.g., of type SOCK_STREAM); the underlying protocol must also support out-of-band data.
The definition of the msghdr structure follows. See recv(2) and below for an exact description of its fields.
struct msghdr {
    void         *msg_name;       /* optional address */
    socklen_t     msg_namelen;    /* size of address */
    struct iovec *msg_iov;        /* scatter/gather array */
    size_t        msg_iovlen;     /* # elements in msg_iov */
    void         *msg_control;    /* ancillary data, see below */
    size_t        msg_controllen; /* ancillary data buffer len */
    int           msg_flags;      /* flags on received message */
};
You may send control information using the msg_control and msg_controllen members. The maximum control buffer length the kernel can process is limited per socket by the value in /proc/sys/net/core/optmem_max; see socket(7).

Return Value

On success, these calls return the number of characters sent. On error, -1 is returned, and errno is set appropriately.

Errors

These are some standard errors generated by the socket layer. Additional errors may be generated and returned from the underlying protocol modules; see their respective manual pages.

EACCES

(For UNIX domain sockets, which are identified by pathname) Write permission is denied on the destination socket file, or search permission is denied for one of the directories the path prefix. (See path_resolution(7).)

EAGAIN or EWOULDBLOCK
The socket is marked nonblocking and the requested operation would block. POSIX.1-2001 allows either error to be returned for this case, and does not require these constants to have the same value, so a portable application should check for both possibilities.
EBADF

An invalid descriptor was specified.

ECONNRESET
Connection reset by peer.
EDESTADDRREQ
The socket is not connection-mode, and no peer address is set.
EFAULT

An invalid user space address was specified for an argument.

EINTR

A signal occurred before any data was transmitted; see signal(7).

EINVAL

Invalid argument passed.

EISCONN
The connection-mode socket was connected already but a recipient was specified. (Now either this error is returned, or the recipient specification is ignored.)
EMSGSIZE
The socket type requires that message be sent atomically, and the size of the message to be sent made this impossible.
ENOBUFS
The output queue for a network interface was full. This generally indicates that the interface has stopped sending, but may be caused by transient congestion. (Normally, this does not occur in Linux. Packets are just silently dropped when a device queue overflows.)
ENOMEM

No memory available.

ENOTCONN
The socket is not connected, and no target has been given.
ENOTSOCK
The argument sockfd is not a socket.
EOPNOTSUPP
Some bit in the flags argument is inappropriate for the socket type.
EPIPE

The local end has been shut down on a connection oriented socket. In this case the process will also receive a SIGPIPE unless MSG_NOSIGNAL is set.

Conforming To

4.4BSD, SVr4, POSIX.1-2001. These function calls appeared in 4.2BSD.

POSIX.1-2001 only describes the MSG_OOB and MSG_EOR flags. POSIX.1-2008 adds a specification of MSG_NOSIGNAL. The MSG_CONFIRM flag is a Linux extension.

Notes

The prototypes given above follow the Single UNIX Specification, as glibc2 also does; the flags argument was int in 4.x BSD, but unsigned int in libc4 and libc5; the len argument was int in 4.x BSD and libc4, but size_t in libc5; the addrlen argument was int in 4.x BSD and libc4 and libc5. See also accept(2).

According to POSIX.1-2001, the msg_controllen field of the msghdr structure should be typed as socklen_t, but glibc currently types it as size_t.

Bugs

Linux may return EPIPE instead of ENOTCONN.

Example

An example of the use of sendto() is shown in getaddrinfo(3).

See Also

fcntl(2), getsockopt(2), recv(2), select(2), sendfile(2), shutdown(2), socket(2), write(2), cmsg(3), ip(7), socket(7), tcp(7), udp(7)

Referenced By

ddp(7), ipv6(7), lwres_getaddrinfo(3), netlink(7), perlfunc(1), raw(7), rds(7), rds-rdma(7), recvmmsg(2), sctp(7), socketcall(2), syscalls(2), unix(7)