Proftp virtualhost configuration 설정 예제(proftpd.conf)

############################################                                                                      
#

#                       ProFTPD Korea User Groups                        
#                           http://proftpd.oops.org                

#                                                                    
############################################

# 번역 : 김정균 < admin@oops.org >
# 이 예제 설정 file은 두개의 가상 server와  anonymous login를 결합하는
# 것을 설명하고 있다.

ServerName                      “ProFTPD”
ServerType                      inetd

# Port 21 은 FTP 의 기본 port이다.
Port                            21

# Global은 main server와 모든 virtualhosts에 의해 공유되는 “global” 설정
# 을 생성한다.

<Global>
  # Umask 022는 group과 world writable로 부터 새로운 dir과 file들을 막기
  # 위한 훌륭한 기본 umask이다.
  Umask                         022
</Global>

# Server가 실행하는데 일반적인 user와 group을 설정한다.
User                            nobody
Group                           nobody

# DoS 공격을 막기 위해, 자식 process의 maximun number를 30으로 설정한다.
# 만약 30이상의 접속을 허락할 필요가 있다면 간단하게 이 치수를 증가시키도록
# 한다. 이것은 오직 standalone mode에서만 가능하다. inetd mode에서는
# service 당 maximun number를 제한 하는 것을 허락하는 inetd server에서
# 설정을 해야 한다.(xintd 역시 마찬가지 이다)
MaxInstances                    30

# 접속에 stall이 될때 끊어 지지 않는 최대 시간을 지정한다.
TimeoutIdle                     300

<VirtualHost ftp.virtual.com>

  ServerName                    “Virtual.com’s FTP Server”

  MaxClients                    10
  MaxLoginAttempts              1

  # DeferWelcome 는 client가 인증을 하기 전에 servername을 display하는
  # 것을 방지한다.
  DeferWelcome                  on

  # guest login만 허락하기를 위하여
  # nomal user login을 제한한다.
  <Limit LOGIN>
    DenyAll
  </Limit>

  # 다음, 자신들의 web site 등에 개인적으로 접근하기 위한 customer에
  # 의해 사용되어 지는 “guest” 계정을 만든다.
  <Anonymous ~cust1>
    User                        cust1
    Group                       cust1
    AnonRequirePassword         on

    <Limit LOGIN>
      AllowAll
    </Limit>

    HideUser                    root
    HideGroup                   root

    # 사용자가 접근하기를 원하지 않는 private directory
    <Directory logs>
      <Limit READ WRITE DIRS>
        DenyAll
      </Limit>
    </Directory>

  </Anonymous>

</VirtualHost>

# primary address에서 작동을 하지만 port는 4000번을 사용하는 또 다른
# virtual host 이다. access는 하나의 anonymous login만 된다.
<VirtualHost our.ip.address>

  ServerName                    “Our private FTP server”
  Port                          4000
  Umask                         027

  <Limit LOGIN>
    DenyAll
  </Limit>

  <Anonymous /usr/local/ftp/virtual/a_customer>

    User                        ftp
    Group                       ftp
    UserAlias                   anonymous ftp

    <Limit LOGIN>
      AllowAll
    </Limit>

    <Limit WRITE>
      DenyAll
    </Limit>

    <Directory incoming>
      <Limit WRITE>
        AllowAll
      </Limit>
    </Directory>

  </Anonymous>

</VirtualHost>

리눅스 압축풀기 명령어

1 tar아카이브(여러 파일을 한 파일로 묶은 것)을 생성하고 푼다.
아카이브의 확장자는 .tar 이다.

tar 는 실행시 반드시 옵션이 있어야 하기 때문에 옵션에 – 를 붙이지 않아도 된다(ps도 마찬가지 이유로 – 가 필요없다). 즉, tar xvf archive.tar 도 작동한다.

2 명령어 활용예
2.1 아카이브 풀기 tar -xvf archive.tar
archive.tar 라는 이름으로 묶인 파일과 디렉토리를 현재 디렉토리에 풀어준다. v 옵션은 파일을 푸는 과정을 화면에 출력해주라는 의미이다.

2.2 아카이브 만들기 tar -cvf archive.tar 묶을파일과디렉토리들
archive.tar 라는 이름으로 파일과 디렉토리를 묶어준다.

2.3 퍼미션을 유지하며 아카이브 만들기 tar -cvfp archive.tar 묶을파일과디렉토리들
archive.tar 라는 이름으로 퍼미션을 유지하며 파일과 디렉토리를 묶어준다.

2.4 tar+gzip 압축하기tar -czvf archive.tar.gz 묶을파일과디렉토리들
archive.tar.gz 라는 이름으로 파일과 디렉토리를 묶어준다. tar -czvfp archive.tar.gz 묶을파일과디렉토리들
archive.tar.gz 라는 이름으로 퍼미션을 유지하며 파일과 디렉토리를 묶어준다.

2.5 tar+bzip2 압축하기tar -cjvf archive.tar.bz 묶을파일과디렉토리들
archive.tar.bz 라는 이름으로 파일과 디렉토리를 묶어준다.

tar -cvjfp archive.tar.gz 묶을파일과디렉토리들
archive.tar.gz 라는 이름으로 퍼미션을 유지하며 파일과 디렉토리를 묶어준다.

2.6 gzip으로 압축된 tar 풀기 tar -xvzf archive.tar.gz
archive.tar.gz 라는 tar 로 묶인뒤 gzip 으로 압축된 파일을 먼저 압축을 푼뒤에 묶인 파일까지 풀어준다.

2.7 bzip2로 압축된 tar 풀기 tar -xvjf arhive.tar.bz
archive.tar.bz 라는 tar 로 묶인뒤 bzip2로 압축된 파일을 먼저 압축을 푼뒤에 묶인 파일까지 풀어준다.

2.8 허가권(Permission) 유지하며 풀기 tar -xpf archive.tar
파일을 묶을 당시의 각 파일의 허가권을 유지하며 풀어준다.

RCS (Revision Control System)

두 명 이상이 참여하는 프로젝트를 진행하고 있다면, 소스 파일의 변경 사항을 적절히 관리하여 변경 사항이 충돌하는 것을 방지하고 지금까지의 변경 사항을 추적하는 것이 중요하다. 소스 파일을 관리하는 데 널리 사용되는 세 가지 시스템은 RCS(Revision Control System), CVS(Current Version System), SCCS(Source Code Control System)이다.

여기에서는 RCS에 관해 간략하게 설명한다.

1.1.1. RCS

RCS는 소스 파일을 관리하는 여러 명령으로 구성되어 있다. 한 파일의 변경 사항을 하나의 파일로 관리하는 방식으로 소스 파일의 변경을 사항을 추적한다.

1.1.1.1. rcs 명령

rsc -i <filename>의 형식으로 명령을 내리면, 파일을 관리하기 위한 초기화 작업이 수행된다. 관리 파일이 생성되는데 원 파일 명 뒤에 ,v가 추가된 형태이다. RCS라는 하위 디렉터리를 만들어두면 관리 파일은 자동으로 이 디렉터리에 저장된다.

1.1.1.2. ci 명령

현재 버전을 저장하는 ci 명령을 사용해 파일을 Check In 할 수 있다. ci <filename>의 형식으로 명령을 내리면 원래 파일은 지워지고 모든 내용과 제어 정보가 RCS파일인 “filename,v” 파일에 들어가게 된다.

1.1.1.3. co 명령

파일을 변경하려면 파일을 Check Out 해야 한다. co -l <filename>을 호출하면 디렉터리에 filename파일이 생기며 CVS 파일은 잠기게 된다. 따라서 다른 사용자가 동시에 파일을 수정하는 것을 방지한다. 수정 후에는 ci를 이용해 다시 Check In 해야 한다. ci -l <filename>의 형식으로 호출하면, 파일을 CVS에 넣고 자동으로 체크아웃 되면서 CVS가 잠긴다. 즉, co -l <filename>을 호출하는 것과 같은 효과가 있다.

1.1.1.4. rolg 명령

rolg <filename> 명령으로 파일의 변경 내역을 볼 수 있다. 파일의 첫 번째 버전으로 돌아가고 싶다면 co -r.1.1 <filename>을 사용할 수 있다. 파일의 버전을 강제로 지정하려면 ci -r2 <filename>과 같은 방법을 사용할 수 있다.

1.1.1.5. rcsdiff 명령

rcsdiff -r1.1 -r1.2 <filename>과 같은 형식으로 두 버전의 차이점을 알아볼 수 있다.

1.1.1.6. 버전 식별

소스 파일에 특수한 매크로를 사용하여 Check In 하면 매크로가 확장되어 내용으로 대체된다. $RCSfile$ 매크로는 파일의 이름으로 확장되고, $Id$ 매크로는 버전을 식별하는 문자열로 확장된다.

1.1.1.7. ident 명령

ident 명령을 사용해 $Id$ 문자열을 포함하는 파일의 버전을 찾을 수 있다. ident <filename>을 입력하면 파일에서 $Id$에 해당하는 문자열 부분을 찾아 출력한다.

Implementing a New Manet Unicast Routing Protocol in NS2 Version 0.2 번역판-4

7 레이어-2 프로토콜들로부터 정보를 받기

몇 개의 라우팅 프로토콜들은 패킷이 레이어-2로부터 보내질 수 없을 때 반응에 흥미가 있을 것이다. 이것은 우리가 아래에 설명한 것처럼, 우리의 라우팅 에이전트에 의해서 쉽게 달성될 수 있다.

그것은 어떻게 작동하는가? 패킷의 공통의 헤더는 그 패킷이 그 레이어-2 에이전트에 의해서 보내질 수 없다면 호출될 것인 함수를 네가 명시할 수 있는 필드를 가진다. 그 함수를 protoname_mac_failed_callback()이라 부르자. 우리는 그런 레이어-2 실패에 대해 반응을 담당하는 역할을 하는 라우팅 에이전트 내에 또 다른 것을 호출하는 이런 함수를 사용할 것이다. 우리는 이런 두 번째 함수를 mac_failed()로 부를 것이다. 그래서 우리는 protoname/protoname.h의 줄 9만 단지 변경해야 한다

protoname/protoname.h

1: class Protoname : public Agent {

2: /* … */

3:

4: public:

5:

6: Protoname(nsaddr_t);

7: int command(int, const char*const*);

8: void recv(Packet*, Handler*);

9: void mac_failed(Packet*);

10: };

11: #endif

protoname/protoname.cc 파일은 더 많은 변화들을 요구한다. 무엇보다도 우리는 공통의 헤더 안쪽에 등록되는 그 함수를 구현해야 한다. 그 함수는 Protoname 클래스의 mac_failed() 함수로 단순히 호출할 것이다. 너는 아래에 구현을 볼 수 있다.

protoname/protoname.cc

1: static void

2: protoname_mac_failed_callback(Packet *p, void *arg) {

3: ((Protoname*)arg)->mac_failed(p);

4: }

mac_failed()에 의해서 구현된 기능성은 protoname 규격에 매우 의존한다. 예를 들어, 코드의 다음 조각은 디버그 메시지를 프린트하고 (줄 6-9) 패킷을 버린다. (줄 11)

protoname/protoname.cc

1: void

2: Protoname::mac_failed(Packet* p) {

3: struct hdr_ip* ih = HDR_IP(p);

4: struct hdr_cmn* ch = HDR_CMN(p);

5:

6: debug(“%f: Node %d MAC layer cannot send a packet to node %dn”,

7: CURRENT_TIME,

8: ra_addr(),

9: ch->next_hop());

10:

11: drop(p, DROP_RTR_MAC_CALLBACK);

12:

13: /* … do something … */

14: }

만약 우리가 라우팅 패킷이 레이어-2 프로토콜들에 의해서 보내지지 않을 때를 알기 원한다면 우리는 send_protoname_pkt()를 변경할 필요가 있다. 유사하게 만약 우리가 데이터 패킷들에 주의하기를 원한다면 forward_data()는 가볍게 마찬가지로 변경되어야 한다. 양쪽의 경우들에서 우리는 그 패킷의 공통의 헤더를 업데이트할 때 다음의 두 개의 줄들을 단지 더해야 한다.

protoname/protoname.cc

1: ch->xmit_failure_ = protoname_mac_failed_callback;

2: ch->xmit_failure_data_ = (void*)this;

protoname_mac_failed_callback()은 무슨 경우들에서 호출될 것인가? NS-2.27에서 우리는 두 개의 다른 상황들을 설립할 수 있다.

mac/arp.cc 노드가 목적지 주소를 (ARP를 통해서) 결정하기를 원하지만 재시도들의 최대 숫자가 초과될 때

mac/mac-802_11.cc 두 개의 가능성들이 있다. 첫 번째 것은 RTS가 보내졌지만 대응하는 CTS가 받아들여지지 않았고 재시도의 최대 숫자가 초과될 때 일어날 수 있다. 두 번째 것은 데이터 패킷이 전달되었지만 절대 ACK를 받지 못했고 (받아들여진 ACK가 없다) 재시도들의 최대 숫자가 초과될 때 발생한다.

8 유선이-붙은-무선 환경들을 위한 지원

이제까지 우리는 평평한 manet들, 즉, 무선-유일한 시나리오들에 대해서만 관련이 있었다. 이번 절에서 우리는 하이브리드 manet들 (NS2 용어를 따르면, 유선이-붙은-무선 시나리오들)을 다루기 위한 기본적인 개념들을 소개할 것이다. 유선이-붙은-무선 스크립트들은 계층적인 주소를 사용할 필요가 있고, 그래서 너는 주소의 이런 유형에서 필요한 지식을 얻기 위해서 15장과 29장 [2]을 읽어야 한다.

최소의 변화들과 함께 우리는 유선이-붙은-무선 시뮬레이션들에서 우리의 프로토콜을 사용할 수 있다. 이런 것들에서는 고정된 노드들, 무선 노드들 그리고 베이스 스테이션들이 있다. 베이스 스테이션은 유선과 무선 도메인들 사이에 게이트웨이이고, 모든 무선 노드는 자신이 어느 베이스 스테이션에 결합되는지를 알 필요가 있다. 우리가 유선이-붙은-무선 지원을 제공하기 위해서 실행할 필요가 있는 모든 것은 각각의 노드를 위해 일치하는 베이스 스테이션을 설정하는 것이다.

유선이-붙은-무선 시나리오들을 기술하는 시뮬레이션 스크립트들은 각각의 모바일 노드에서 시간적으로 앞선 동작을 수행하는데, 즉, 모든 모바일 노드는 베이스 스테이션 (노드 API베이스-스테이션 함수)에 부착된다. 그러나 우리는 여러 개의 베이스 스테이션들이 사용되는 시나리오들에 관심이 있고, 우리는 모바일 노드들이 그들의 결합된 베이스 스테이션들을 동적으로 바꾸기를 원한다는 것을 상상해라. 우리가 다수의 베이스 스테이션들이 허락되는 하이브리드 ad hoc 네트워크들을 지원하는 라우팅 프로토콜을 코드하기를 원한다면 이것은 유용하다. 만약 이것이 너의 경우라면, 그 절을 계속해서 읽어라.

다음의 코드에서 보여지는 것처럼 protoname/protoname.h를 다시 편집하자. 줄들 1과 11은 더해지고, 반면에 나머지는 변하지 않는다.

protoname/protoname.h

1: #include <mobilenode.h>

2:

3: /* … */

4:

5: class Protoname : public Agent {

6:

7: /* … */

8:

9: protected:

10:

11: MobileNode* node_;

12:

13: /* … */

14: };

우리는 라우팅 에이전트가 부착되는 노드를 의미하는 (common/mobilenode.h에서 정의된) 모바일노드 오브젝트에 대한 참조를 더했다. 이런 참조를 얻기 위해서 우리는 Protoname 건설자 안쪽에 다음의 줄 4를 더할 필요가 있다.

protoname/protoname.cc

1: Protoname::Protoname(nsaddr_t id) : Agent(PT_PROTONAME), pkt_timer_(this) {

2: bind_bool(“accessible_var_”, &accessible_var_);

3: ra_addr_ = id;

4: node_ = (MobileNode*)Node::get_node_by_address(id);

5: }

모바일노드 클래스는 우리가 관심이 있는 두 개의 함수들을 소유한다. 우선 첫째로 모바일 노드가 부착되는 베이스 스테이션의 식별자를 되돌려주는, base_stn()이다. 두 번째는 그 모바일 노드를 위한 적합한 베이스 스테이션을 설립할 수 있는 set_base_stn()이다. 그래서 우리는 유선이-붙은-무선 시뮬레이션들을 이런 두 개의 함수들을 사용함으로써 다룰 수 있다. 예를 들어, 다음의 코드는 그 모바일 노드 자체가 베이스 스테이션인지를 체크하고; 만약 아니라면 그때 그것은 베이스 스테이션을 할당 받는다.

protoname/protoname.cc

1: if (node_->base_stn() == ra_addr()) {

2: // I’m a base station

3: /* … */

4: }

5: else {

6: // I’m not a base station

7: node_->set_base_stn(base_stn_addr);

8: }

앞의 예제는 결합된 베이스 스테이션을 동적으로 바꾸는 법을 보여준다. 이런 바꿈들을 수행하기 위해서 사용되는 접근들은 프로토콜 자체에 의존한다.

참조

[1] Marc Greis. Tutorial for the Network Simulator ”ns”.

http://www.isi.edu/nsnam/ns/tutorial/index.html.

[2] The VINT Project. The ns Manual, December 2003.

http://www.isi.edu/nsnam/ns/ns-documentation.html.


[1] 이것은 manet 라우팅 프로토콜들에서 일반적인 특징이지만, 그러나 실제 목적은 랜덤 숫자들을 얻는 예제를 제공하는 것이다.

[2] 실제 이것은 사실이 아니다. 사실 데이터 패킷들은 자신들의 일치하는 에이전트에게 직접 배달되고, 그래서 포트 분류자는 우리의 라우팅 에이전트에서 필요하지 않다. 그러나 우리는 이런 설명을 유지하는데 왜냐하면 NS-2.27은 라우팅 에이전트가 자신의 API의 부분으로써 port-dmux 동작 (4.3.2절을 보라)을 받아들이는 것을 요구하기 때문이다.

[3] IP 헤더에 따라서 패킷의 Time To Live

Implementing a New Manet Unicast Routing Protocol in NS2 Version 0.2 번역판-3

5 라우팅 표

너는 라우팅 표를 필요로 하지 않을지라도, 만약 너의 프로토콜이 그것을 사용한다면 그때 이 절을 읽어라. 우리는 다른 클래스로써 또는 임의의 다른 데이터 구조 (예를 들어, 해쉬 표)로써 라우팅 표를 구현할 수 있다. 우리는 라우팅 표가 가지기로 되어 있는 그 기능성을 캡슐화하는 클래스를 보일 예정이다. 내부의 정보는 프로토콜에서 프로토콜까지 많이 변할 것이다. 라우팅 표에서 각각의 입력을 위해서 목적지 주소들, 다음 홉 주소들, 루트들과 결합된 거리들 또는 비용, 시퀀스 넘버들, 수명들 기타 등등을 저장하기를 원할 것이다. 물론 우리의 예제는 매우 단순한 라우팅 표와 그것을 프린트하기 위한 방법을 묘사한다. 우리가 각각의 입력에 저장할 유일한 정보는 목적지와 다음 홉 주소들이다. 우리는 저장 구조로써 해시 표 (map)을 사용한다. 이런 경우는 너무 단순해서 새로운 클래스를 구현할 수 없지만, 우리는 예제로써 그것을 실행할 것이다. 코드의 다음 조각은 protoname/protoname_rtable.h에 일치한다.

protoname/protoname_rtable.h

1: #ifndef __protoname_rtable_h__

2: #define __protoname_rtable_h__

3:

4: #include <trace.h>

5: #include <map>

6:

7: typedef std::map<nsaddr_t, nsaddr_t> rtable_t;

8:

9: class protoname_rtable {

10:

11: rtable_t rt_;

12:

13: public:

14:

15: protoname_rtable();

16: void print(Trace*);

17: void clear();

18: void rm_entry(nsaddr_t);

19: void add_entry(nsaddr_t, nsaddr_t);

20: nsaddr_t lookup(nsaddr_t);

21: u_int32_t size();

22: };

23:

24: #endif

이런 함수들의 구현은 매우 쉽다. 사실 그 건설자는 매우 단순해서 그것 안에 실행할 것이 아무것도 없다.

protoname/protoname_rtable.cc

1: protoname_rtable::protoname_rtable() { }

print() 함수는 노드의 라우팅 표의 내용들을 트레이스 파일로 덤프할 것이다. 그것을 실행하기 위해서 우리는 절 4.3에서 언급했던 트레이스 클래스를 사용한다.

protoname/protoname_rtable.cc

1: void

2: protoname_rtable::print(Trace* out) {

3: sprintf(out->pt_->buffer(), “Ptdesttnext”);

4: out->pt_->dump();

5: for (rtable_t::iterator it = rt_.begin(); it != rt_.end(); it++) {

6: sprintf(out->pt_->buffer(), “Pt%dt%d”,

7: (*it).first,

8: (*it).second);

9: out->pt_->dump();

10: }

11: }

다음의 함수는 라우팅 표 안에 모든 입력들을 제거한다.

protoname/protoname_rtable.cc

1: void

2: protoname_rtable::clear() {

3: rt_.clear();

4: }

그것의 목적지 주소가 주어진 입력을 제거하기 위해서 우리는 rm_entry() 함수를 구현한다.

protoname/protoname_rtable.cc

1: void

2: protoname_rtable::rm_entry(nsaddr_t dest) {

3: rt_.erase(dest);

4: }

아래의 코드는 그것의 목적지와 다음 홉 주소들이 주어진 라우팅 표 안에 새로운 입력을 더하기 위해서 사용된다.

protoname/protoname_rtable.cc

1: void

2: protoname_rtable::add_entry(nsaddr_t dest, nsaddr_t next) {

3: rt_[dest] = next;

4: }

Lookup()은 그것의 목적지 주소가 주어진 입력의 다음 홉 주소를 되돌려준다. 만약 그런 입력이 존재하지 않는다면, (즉, 그 목적지를 위한 루트가 없다면) 그 함수는 IP_BROADCAST를 되돌려준다. 물론 이 상수를 사용하기 위해서 common/ip.h를 포함한다.

protoname/protoname_rtable.cc

1: nsaddr_t

2: protoname_rtable::lookup(nsaddr_t dest) {

3: rtable_t::iterator it = rt_.find(dest);

4: if (it == rt_.end())

5: return IP_BROADCAST;

6: else

7: return (*it).second;

8: }

마지막으로, size()는 라우팅 표 안에 입력들의 수를 되돌려준다.

protoname/protoname_rtable.cc

1: u_int32_t

2: protoname_rtable::size() {

3: return rt_.size();

4: }

6 요구되는 변화들

우리는 거의 끝냈다. 우리는 NS2 안쪽에 프로토콜 protoname을 위한 라우팅 에이전트를 구현해왔다. 그러나 우리의 코드를 시뮬레이터 안쪽에 통합하기 위해서 우리가 실행할 필요가 있는 몇 가지 변화들이 있다.

6.1 패킷 유형 선언

만약 우리가 기억한다면 우리는 우리의 새로운 패킷 유형, PT_PROTONAME을 가리키기 위한 상수를 사용해야 했었다. 우리는 파일 common/packet.h 안쪽에 그것을 정의할 것이다.

모든 패킷 유형들이 리스트된, packet_h 목록을 찾아보자. 우리는 코드의 다음 조각에서 보이는 것처럼 PT_PROTONAME을 이런 목록에 더할 것이다. (줄 6)

common/packet.h

1: enum packet_t {

2: PT_TCP,

3: PT_UDP,

4: PT_CBR,

5: /* … much more packet types … */

6: PT_PROTONAME,

7: PT_NTYPE // This MUST be the LAST one

8: };

바로 아래에 같은 파일에 p_info 클래스의 정의가 있다. 건설자 안쪽에 우리는 우리의 패킷 유형을 위한 본문의 이름을 제공할 것이다. (줄 6)

common/packet.h

1: p_info() {

2: name_[PT_TCP]= “tcp”;

3: name_[PT_UDP]= “udp”;

4: name_[PT_CBR]= “cbr”;

5: /* … much more names … */

6: name_[PT_PROTONAME]= “protoname”;

7: }

6.2 트레이싱 지원

우리가 알다시피 시뮬레이션의 목적은 실행 동안에 무엇이 일어났는지 기술하는 트레이스 파일을 얻는 것이다. 트레이스들에 친밀함을 느끼기 위해서 23장 [2]를 읽어봐라. 트레이스 오브젝트는 패킷이 받아들여지거나, 보내지거나, 또는 버려질 때마다 매번 패킷의 요구되는 정보를 쓰기 위해서 사용된다. 우리의 패킷 유형에 관한 정보를 기록하기 위해서 우리는 CMUTrace 클래스 안쪽에 format_protoname() 함수를 구현한다. 무선 시뮬레이션들을 위한 트레이스 지원은 CMUTrace 오브젝트들에 의해서 제공되고 그것은 16장 [2]에서 기술된다.

trace/cmu-trace.h 파일을 편집해보자. 우리는 다음의 예제의 줄 수 6 안에서처럼 우리의 새로운 함수를 더해야 한다.

trace/cmu-trace.h

1: class CMUTrace : public Trace {

2: /* … definitions … */

3: private:

4: /* … */

5: void format_aodv(Packet *p, int offset);

6: void format_protoname(Packet *p, int offset);

7: };

(trace/cmu-trace.cc로부터 추출된) 코드의 다음 조각은 트레이스들의 다른 유형들을 보여준다.

trace/cmu-trace.cc

1: #include <protoname/protoname_pkt.h>

2:

3: /* … */

4:

5: void

6: CMUTrace::format_protoname(Packet *p, int offset)

7: {

8: struct hdr_protoname_pkt* ph = HDR_PROTONAME_PKT(p);

9:

10: if (pt_->tagged()) {

11: sprintf(pt_->buffer() + offset,

12: “-protoname:o %d -protoname:s %d -protoname:l %d “,

13: ph->pkt_src(),

14: ph->pkt_seq_num(),

15: ph->pkt_len());

16: }

17: else if (newtrace_) {

18: sprintf(pt_->buffer() + offset,

19: “-P protoname -Po %d -Ps %d -Pl %d “,

20: ph->pkt_src(),

21: ph->pkt_seq_num(),

22: ph->pkt_len());

23: }

24: else {

25: sprintf(pt_->buffer() + offset,

26: “[protoname %d %d %d] “,

27: ph->pkt_src(),

28: ph->pkt_seq_num(),

29: ph->pkt_len());

30: }

31: }

우리는 위의 코드로부터 3가지 다른 트레이스 포맷들이 있다는 것을 추론할 수 있다: 태그가 붙은 트레이스들, 새로운 포맷 트레이스들 그리고 고전적인 트레이스들. 문법에 이어서 각각은, 비록 다르지만, 네가 말할 수 있는 것처럼 매우 쉽고 직관적이다. 태그가 붙은 것과 새로운 트레이스 포맷들 양쪽에는 프린트되는 중인 정보의 각각의 필드를 식별하기 위해서 사용되는 표지들이 존재한다. 우리는 소스 주소로써 “o” (기원), 시퀀스 넘버로써 “s”, 일치하는 패킷의 길이로써 “l”을 사용하기로 결정해왔다.

이것을 최근에 생성된 함수로 호출하기 위해서 우리는 trace/cmu-trace.cc 안에 format()을 바꿔야 한다.

trace/cmu-trace.cc

1: void

2: CMUTrace::format(Packet* p, const char *why)

3: {

4: /* … */

5: case PT_PING:

6: break;

7:

8: case PT_PROTONAME:

9: format_protoname(p, offset);

10: break;

11:

12: default:

13: /* … */

14: }

6.3 Tcl 라이브러리

이제 우리는 Tcl 파일들에서 몇 개의 변화들을 실행할 필요가 있다. 실제로 우리는 우리의 패킷 유형을 더하고, 묶여진 속성들을 위한 초기 값들을 주고 우리의 protoname 라우팅 프로토콜을 실행하는 무선 노드들을 생성하기 위한 요구되는 인프라스트럭처를 제공할 예정이다.

tcl/lib/ns-packet.tcl에서 우리는 다음의 코드의 위치를 정해야 하고 protoname을 목록에 더해야 한다. (우리가 줄 2에서 실행한 것처럼)

tcl/lib/ns-packet.tcl

1: foreach prot {

2: Protoname

3: AODV

4: ARP

5: # …

6: NV

7: } {

8: add-packet-header $prot

9: }

묶여진 속성들을 위한 초기 값들은 tcl/lib/ns-packet.tcl 안쪽에 주어져야 한다. 우리는 파일의 끝으로 가야하고 다음의 코드와 같이 무언가를 써야 한다:

tcl/lib/ns-default.tcl

1: # …

2: # Defaults defined for Protoname

3: Agent/Protoname set accessible_var_ true

마지막으로 우리는 tcl/lib/ns-lib.tcl을 변경해야 한다. 우리는 노드를 생성하기 위한 프로시저들을 더할 필요가 있다. 우리의 흥미는 라우팅 프로토콜로써 protoname을 가진 무선 노드를 생성하는 것을 중심으로 행해질 것이다.

프로시저 노드create-wireless-node 프로시저에게로 호출한다. 다른 업무들 중에서, 이 마지막 것은, 하나의 노드를 위한 그 라우팅 에이전트를 설정하려는 의도이다. 우리는 우리의 protoname 프로토콜의 인스턴스를 생성하기 위해서 이런 프로시저를 교묘히 바꿀 필요가 있다.

tcl/lib/ns-lib.tcl

1: Simulator instproc create-wireless-node args {

2: # …

3: switch -exact $routingAgent_ {

4: Protoname {

5: set ragent [$self create-protoname-agent $node]

6: }

7: # …

8: }

9: # …

10: }

그리고 나서 create-protoname-agent는 다음의 예제에서 보여지는 것처럼 아래에 코드될 것이다.

tcl/lib/ns-lib.tcl

1: Simulator instproc create-protoname-agent { node } {

2: # Create Protoname routing agent

3: set ragent [new Agent/Protoname [$node node-addr]]

4: $self at 0.0 “$ragent start”

5: $node set ragent_ $ragent

6: return $ragent

7: }

줄 3은 노드의 주소를 가진 새로운 protoname 에이전트를 생성한다. 이런 에이전트는 시뮬레이션의 처음에 시작되도록 스케쥴되고, (줄 4) 줄 5에서 노드의 라우팅 에이전트로써 할당된다.

6.4 우선순위 큐

네가 너의 시뮬레이션들에서 우선순위 큐들을 사용할 것은 매우 적당하다. 이 큐 유형은 높은 우선순위 패킷들로써 라우팅 패킷들을 취급하고, 그것들을 큐의 처음에 삽입한다. 그러나 우리는 protoname 패킷들이 라우팅 패킷들이고 그래서 높은 우선순위로써 취급된다고 PriQueue 클래스에게 말할 필요가 있다.

우리는 queue/priqueue.cc 파일에 recv() 함수를 변경해야 한다. 코드의 다음 조각에서 줄 13은 우리가 실행할 필요가 있는 유일한 변경이다.

queue/priqueue.cc

1: void

2: PriQueue::recv(Packet *p, Handler *h)

3: {

4: struct hdr_cmn *ch = HDR_CMN(p);

5:

6: if (Prefer_Routing_Protocols) {

7:

8: switch(ch->ptype()) {

9: case PT_DSR:

10: case PT_MESSAGE:

11: case PT_TORA:

12: case PT_AODV:

13: case PT_PROTONAME:

14: recvHighPriority(p, h);

15: break;

16:

17: default:

18: Queue::recv(p, h);

19: }

20: }

21: else {

22: Queue::recv(p, h);

23: }

24: }

6.5 Makefile

이제 모든 것이 구현되고 우리는 단지 그것을 컴파일할 필요가 있다! 그렇게 하기 위해서 우리는 다음의 코드 (줄 4)에서처럼 OBJ_CC 변수 안쪽에 우리의 오브젝트 파일들을 더함으로써 Makefile 파일을 편집할 것이다.

Makefile

1: OBJ_CC =

2: tools/random.o tools/rng.o tools/ranvar.o common/misc.o common/timer-handler.o

3: # …

4: protoname/protoname.o protoname/protoname_rtable.o

5: # …

6: $(OBJ_STL)

우리가 common/packet.h를 변경했지만 common/packet.cc는 변경하지 않았기 때문에 우리는 다시 컴파일되는 것을 위해서 이 마지막 파일을 “touch”해야 한다. 그런 후에 우리는 make를 실행할 수 있고 우리 소유의 라우팅 프로토콜을 즐길 수 있다. (또는 아마 모든 편집 문제들을 해결할 수 있다!)

[ns-2.27]$ touch common/packet.cc

[ns-2.27]$ make