diff --git a/doc/tutorial/Makefile b/doc/tutorial/Makefile index a076e334f..b229ba74c 100644 --- a/doc/tutorial/Makefile +++ b/doc/tutorial/Makefile @@ -2,13 +2,21 @@ TEXI2HTML = texi2html TEXI2PDF = texi2dvi --pdf EPSTOPDF = epstopdf TGIF = tgif +DIA = dia CONVERT = convert CSS = --css-include=tutorial.css SPLIT = --split section -TGIF_SOURCES = helpers.obj +DIA_SOURCES = buffer.dia pp.dia dumbbell.dia star.dia +TGIF_SOURCES = packet.obj helpers.obj + +DIA_EPS = ${DIA_SOURCES:.dia=.eps} +DIA_PNG = ${DIA_SOURCES:.dia=.png} +DIA_PDF = ${DIA_SOURCES:.dia=.pdf} + TGIF_EPS = ${TGIF_SOURCES:.obj=.eps} -TGIF_PNG = ${TGIF_SOURCES:.obj=.eps} +TGIF_PNG = ${TGIF_SOURCES:.obj=.png} +TGIF_PDF = ${TGIF_SOURCES:.obj=.pdf} all: images html split-html pdf @@ -17,8 +25,11 @@ all: images html split-html pdf # buffer may be needed (xorg-x11-server-Xvfb) to provide a "fake" # display images: - cd figures/; $(TGIF) -print -eps $(TGIF_SOURCES) + cd figures/; $(DIA) -t png $(DIA_SOURCES) + cd figures/; $(DIA) -t eps $(DIA_SOURCES) + cd figures/; $(foreach FILE,$(DIA_EPS),$(EPSTOPDF) $(FILE);) cd figures/; $(TGIF) -print -png $(TGIF_SOURCES) + cd figures/; $(TGIF) -print -eps $(TGIF_SOURCES) cd figures/; $(foreach FILE,$(TGIF_EPS),$(EPSTOPDF) $(FILE);) html: images @@ -30,5 +41,9 @@ split-html: images pdf: images $(TEXI2PDF) tutorial.texi -clean: +figures-clean: + cd figures/; rm -rf $(DIA_EPS); rm -rf $(DIA_PNG); rm -rf $(DIA_PDF) + cd figures/; rm -rf $(TGIF_EPS); rm -rf $(TGIF_PNG); rm -rf $(TGIF_PDF) + +clean: figures-clean rm -rf tutorial.aux tutorial.cp tutorial.cps tutorial.fn tutorial.ky tutorial.pg tutorial.tp tutorial.vr tutorial.toc tutorial.log tutorial.pdf tutorial.html tutorial/ diff --git a/doc/tutorial/dumbbell.png b/doc/tutorial/dumbbell.png deleted file mode 100644 index 0fc3d8c9e..000000000 Binary files a/doc/tutorial/dumbbell.png and /dev/null differ diff --git a/doc/tutorial/figures/README b/doc/tutorial/figures/README index fe69d95cb..4f63268dc 100644 --- a/doc/tutorial/figures/README +++ b/doc/tutorial/figures/README @@ -1,8 +1,17 @@ Please write image files in a vector graphics format, when possible, and generate the .png and .pdf versions on the fly (see ../Makefile). -Recommended tools are dia, tgif, and xfig. Store the .dia, .obj, or .fig -versions in mercurial, but not the .png or .pdfs. +Currently supported tools are dia and tgif. xfig could be added similarly +if someone wants to add it. The main requirement for adding another format +is that the tool to edit it is freely available and that a cron script can +autogenerate the pdf and png from the figure source. + +Store the .dia, or .obj versions in mercurial, but not the .png or .pdfs. +If the figure is not available in a vector graphics format, store both +a .png and a .pdf version in this directory. + +If you add a source (.dia, .obj) file here, remember to add it to +the list of figure sources in the Makefile in the directory above Note: tgif can convert from .obj to .pdf, but the pdf that results takes up a whole page. Instead, we convert to an intermediate .eps step, and diff --git a/doc/tutorial/figures/buffer.dia b/doc/tutorial/figures/buffer.dia new file mode 100644 index 000000000..b1868f7f4 --- /dev/null +++ b/doc/tutorial/figures/buffer.dia @@ -0,0 +1,1623 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Count# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Size# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Initial Start# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Dirty Start# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Unused Area# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Dirty Size# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Dirty Area# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Virtual Zero Area# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Unused Area# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Data# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Zero Area Size# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Used start# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Used Size# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Data# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Zero Area Size# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Used start# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Used Size# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Used# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Used# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Virtual Zero Area# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Buffer# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #BufferData# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Buffer# + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/tutorial/figures/dumbbell.dia b/doc/tutorial/figures/dumbbell.dia new file mode 100644 index 000000000..91af27577 Binary files /dev/null and b/doc/tutorial/figures/dumbbell.dia differ diff --git a/doc/tutorial/oneobj.png b/doc/tutorial/figures/oneobj.png similarity index 100% rename from doc/tutorial/oneobj.png rename to doc/tutorial/figures/oneobj.png diff --git a/doc/tutorial/figures/packet.obj b/doc/tutorial/figures/packet.obj new file mode 100644 index 000000000..c6a66cd71 --- /dev/null +++ b/doc/tutorial/figures/packet.obj @@ -0,0 +1,227 @@ +%TGIF 4.1.43-QPL +state(0,37,100.000,0,64,0,32,0,9,1,1,1,0,0,0,1,0,'Courier-Bold',1,103680,0,3,0,10,0,0,1,1,0,16,0,0,1,1,1,1,1088,1408,1,0,2880,0). +% +% @(#)$Header$ +% %W% +% +unit("1 pixel/pixel"). +color_info(11,65535,0,[ + "magenta", 65535, 0, 65535, 65535, 0, 65535, 1, + "red", 65535, 0, 0, 65535, 0, 0, 1, + "green", 0, 65535, 0, 0, 65535, 0, 1, + "blue", 0, 0, 65535, 0, 0, 65535, 1, + "yellow", 65535, 65535, 0, 65535, 65535, 0, 1, + "pink", 65535, 49344, 52171, 65535, 49344, 52171, 1, + "cyan", 0, 65535, 65535, 0, 65535, 65535, 1, + "CadetBlue", 24415, 40606, 41120, 24415, 40606, 41120, 1, + "white", 65535, 65535, 65535, 65535, 65535, 65535, 1, + "black", 0, 0, 0, 0, 0, 0, 1, + "DarkSlateGray", 12079, 20303, 20303, 12079, 20303, 20303, 1 +]). +script_frac("0.6"). +fg_bg_colors('black','white'). +dont_reencode("FFDingbests:ZapfDingbats"). +page(1,"",1,''). +box('black','',32,48,240,256,0,3,1,0,0,0,0,0,0,'3',0,[ +]). +text('black',64,10,1,0,1,121,28,3,22,6,0,0,0,0,2,121,28,0,0,"",0,0,0,0,32,'',[ +minilines(121,28,0,0,0,0,0,[ +mini_line(121,22,6,0,0,0,[ +str_block(0,121,22,6,0,0,0,0,0,[ +str_seg('black','Times-Roman',0,138240,121,22,6,0,0,0,0,0,0,0, + "class Packet")]) +]) +])]). +text('black',416,100,1,0,1,116,28,15,22,6,0,0,0,0,2,116,28,0,0,"",0,0,0,0,122,'',[ +minilines(116,28,0,0,0,0,0,[ +mini_line(116,22,6,0,0,0,[ +str_block(0,116,22,6,0,0,0,0,0,[ +str_seg('black','Times-Roman',0,138240,116,22,6,0,0,0,0,0,0,0, + "class Buffer")]) +]) +])]). +text('black',48,178,4,0,1,83,69,32,14,4,0,0,0,0,2,83,69,0,0,"",0,0,0,0,192,'',[ +minilines(83,69,0,0,0,0,0,[ +mini_line(80,14,4,0,0,0,[ +str_block(0,80,14,4,0,-1,0,0,0,[ +str_seg('black','Times-Bold',1,80640,80,14,4,0,-1,0,0,0,0,0, + "private data:")]) +]), +mini_line(59,14,3,0,0,0,[ +str_block(0,59,14,3,0,0,0,0,0,[ +str_seg('black','Times-Roman',0,80640,59,14,3,0,0,0,0,0,0,0, + "- unique id")]) +]), +mini_line(83,14,3,0,0,0,[ +str_block(0,83,14,3,0,0,0,0,0,[ +str_seg('black','Times-Roman',0,80640,83,14,3,0,0,0,0,0,0,0, + "- Buffer object")]) +]), +mini_line(76,14,3,0,0,0,[ +str_block(0,76,14,3,0,0,0,0,0,[ +str_seg('black','Times-Roman',0,80640,76,14,3,0,0,0,0,0,0,0, + "- Tags object")]) +]) +])]). +text('black',112,288,1,0,1,103,28,82,22,6,0,0,0,0,2,103,28,0,0,"",0,0,0,0,310,'',[ +minilines(103,28,0,0,0,0,0,[ +mini_line(103,22,6,0,0,0,[ +str_block(0,103,22,6,0,-1,0,0,0,[ +str_seg('black','Times-Roman',0,138240,103,22,6,0,-1,0,0,0,0,0, + "class Tags")]) +]) +])]). +text('black',48,50,5,0,1,175,86,176,14,4,0,0,0,0,2,175,86,0,0,"",0,0,0,0,64,'',[ +minilines(175,86,0,0,0,0,0,[ +mini_line(105,14,4,0,0,0,[ +str_block(0,105,14,4,0,-1,0,0,0,[ +str_seg('black','Times-Bold',1,80640,105,14,4,0,-1,0,0,0,0,0, + "public functions:")]) +]), +mini_line(80,14,3,0,0,0,[ +str_block(0,80,14,3,0,-1,0,0,0,[ +str_seg('black','Times-Roman',0,80640,80,14,3,0,-1,0,0,0,0,0, + "- constructors")]) +]), +mini_line(175,14,3,0,0,0,[ +str_block(0,175,14,3,0,-1,0,0,0,[ +str_seg('black','Times-Roman',0,80640,175,14,3,0,-1,0,0,0,0,0, + "- add/remove/peek at Headers")]) +]), +mini_line(155,14,3,0,0,0,[ +str_block(0,155,14,3,0,-1,0,0,0,[ +str_seg('black','Times-Roman',0,80640,155,14,3,0,-1,0,0,0,0,0, + "- add/remove/peek at Tags")]) +]), +mini_line(88,14,3,0,0,0,[ +str_block(0,88,14,3,0,0,0,0,0,[ +str_seg('black','Times-Roman',0,80640,88,14,3,0,0,0,0,0,0,0, + "- fragmentation")]) +]) +])]). +box('black','',384,144,614,352,0,3,1,245,0,0,0,0,0,'3',0,[ +]). +text('black',400,274,4,0,1,204,69,246,14,4,0,0,0,0,2,204,69,0,0,"",0,0,0,0,288,'',[ +minilines(204,69,0,0,0,0,0,[ +mini_line(80,14,4,0,0,0,[ +str_block(0,80,14,4,0,-1,0,0,0,[ +str_seg('black','Times-Bold',1,80640,80,14,4,0,-1,0,0,0,0,0, + "private data:")]) +]), +mini_line(193,14,3,0,0,0,[ +str_block(0,193,14,3,0,0,0,0,0,[ +str_seg('black','Times-Roman',0,80640,193,14,3,0,0,0,0,0,0,0, + "- struct BufferData, a dynamically")]) +]), +mini_line(160,14,3,0,0,0,[ +str_block(0,160,14,3,0,0,0,0,0,[ +str_seg('black','Times-Roman',0,80640,160,14,3,0,0,0,0,0,0,0, + "varying byte buffer to which")]) +]), +mini_line(204,14,3,0,0,0,[ +str_block(0,204,14,3,0,0,0,0,0,[ +str_seg('black','Times-Roman',0,80640,204,14,3,0,0,0,0,0,0,0, + "data can be prepended or appended")]) +]) +])]). +text('black',400,146,5,0,1,188,86,247,14,4,0,0,0,0,2,188,86,0,0,"",0,0,0,0,160,'',[ +minilines(188,86,0,0,0,0,0,[ +mini_line(105,14,4,0,0,0,[ +str_block(0,105,14,4,0,-1,0,0,0,[ +str_seg('black','Times-Bold',1,80640,105,14,4,0,-1,0,0,0,0,0, + "public functions:")]) +]), +mini_line(172,14,3,0,0,0,[ +str_block(0,172,14,3,0,0,0,0,0,[ +str_seg('black','Times-Roman',0,80640,172,14,3,0,0,0,0,0,0,0, + "- Iterators to move byte buffer")]) +]), +mini_line(171,14,3,0,0,0,[ +str_block(0,171,14,3,0,0,0,0,0,[ +str_seg('black','Times-Roman',0,80640,171,14,3,0,0,0,0,0,0,0, + "pointers forward or backward")]) +]), +mini_line(188,14,3,0,0,0,[ +str_block(0,188,14,3,0,0,0,0,0,[ +str_seg('black','Times-Roman',0,80640,188,14,3,0,0,0,0,0,0,0, + "- functions to read and write data")]) +]), +mini_line(132,14,3,0,0,0,[ +str_block(0,132,14,3,0,-1,0,0,0,[ +str_seg('black','Times-Roman',0,80640,132,14,3,0,-1,0,0,0,0,0, + "of various sized chunks")]) +]) +])]). +box('black','',96,324,304,532,0,3,1,264,0,0,0,0,0,'3',0,[ +]). +text('black',112,454,4,0,1,167,69,265,14,4,0,0,0,0,2,167,69,0,0,"",0,0,0,0,468,'',[ +minilines(167,69,0,0,0,0,0,[ +mini_line(80,14,4,0,0,0,[ +str_block(0,80,14,4,0,-1,0,0,0,[ +str_seg('black','Times-Bold',1,80640,80,14,4,0,-1,0,0,0,0,0, + "private data:")]) +]), +mini_line(167,14,3,0,0,0,[ +str_block(0,167,14,3,0,0,0,0,0,[ +str_seg('black','Times-Roman',0,80640,167,14,3,0,0,0,0,0,0,0, + "- singly linked-list of TagData")]) +]), +mini_line(158,14,3,0,0,0,[ +str_block(0,158,14,3,0,0,0,0,0,[ +str_seg('black','Times-Roman',0,80640,158,14,3,0,0,0,0,0,0,0, + "structures, with a reference")]) +]), +mini_line(32,14,3,0,0,0,[ +str_block(0,32,14,3,0,0,0,0,0,[ +str_seg('black','Times-Roman',0,80640,32,14,3,0,0,0,0,0,0,0, + "count")]) +]) +])]). +text('black',112,326,5,0,1,155,86,266,14,4,0,0,0,0,2,155,86,0,0,"",0,0,0,0,340,'',[ +minilines(155,86,0,0,0,0,0,[ +mini_line(105,14,4,0,0,0,[ +str_block(0,105,14,4,0,-1,0,0,0,[ +str_seg('black','Times-Bold',1,80640,105,14,4,0,-1,0,0,0,0,0, + "public functions:")]) +]), +mini_line(80,14,3,0,0,0,[ +str_block(0,80,14,3,0,-1,0,0,0,[ +str_seg('black','Times-Roman',0,80640,80,14,3,0,-1,0,0,0,0,0, + "- constructors")]) +]), +mini_line(155,14,3,0,0,0,[ +str_block(0,155,14,3,0,-1,0,0,0,[ +str_seg('black','Times-Roman',0,80640,155,14,3,0,-1,0,0,0,0,0, + "- templates to add, remove,")]) +]), +mini_line(148,14,3,0,0,0,[ +str_block(0,148,14,3,0,-1,0,0,0,[ +str_seg('black','Times-Roman',0,80640,148,14,3,0,-1,0,0,0,0,0, + "or peek at Tags of various")]) +]), +mini_line(31,14,3,0,0,0,[ +str_block(0,31,14,3,0,-1,0,0,0,[ +str_seg('black','Times-Roman',0,80640,31,14,3,0,-1,0,0,0,0,0, + "types")]) +]) +])]). +poly('black','',2,[ + 59,245,96,320],0,2,1,272,0,0,3,0,0,0,0,'2',0,0, + "0","",[ + 0,10,4,0,'10','4','0'],[0,10,4,0,'10','4','0'],[ +]). +poly('black','',2,[ + 123,246,288,320],0,2,1,280,0,0,3,0,0,0,0,'2',0,0, + "0","",[ + 0,10,4,0,'10','4','0'],[0,10,4,0,'10','4','0'],[ +]). +poly('black','',2,[ + 141,219,379,147],0,2,1,286,0,0,3,0,0,0,0,'2',0,0, + "0","",[ + 0,10,4,0,'10','4','0'],[0,10,4,0,'10','4','0'],[ +]). +poly('black','',2,[ + 132,226,375,335],0,2,1,287,0,0,3,0,0,0,0,'2',0,0, + "0","",[ + 0,10,4,0,'10','4','0'],[0,10,4,0,'10','4','0'],[ +]). diff --git a/doc/tutorial/figures/pp.dia b/doc/tutorial/figures/pp.dia new file mode 100644 index 000000000..2c81980a5 Binary files /dev/null and b/doc/tutorial/figures/pp.dia differ diff --git a/doc/tutorial/figures/star.dia b/doc/tutorial/figures/star.dia new file mode 100644 index 000000000..6b826b313 Binary files /dev/null and b/doc/tutorial/figures/star.dia differ diff --git a/doc/tutorial/threeobj.png b/doc/tutorial/figures/threeobj.png similarity index 100% rename from doc/tutorial/threeobj.png rename to doc/tutorial/figures/threeobj.png diff --git a/doc/tutorial/other.texi b/doc/tutorial/other.texi index efa83a083..3e4a63a7b 100644 --- a/doc/tutorial/other.texi +++ b/doc/tutorial/other.texi @@ -34,7 +34,7 @@ see this point-to-point network, you can think of an RS-422 (or RS-232 for you old-timers) cable. This topology is shown below. @sp 1 -@center @image{pp,,,,png} +@center @image{figures/pp,,,,png} @cindex CreateObject @cindex InternetNode @@ -173,7 +173,7 @@ a file for you in the @code{tutorial} directory called @code{tutorial-star.cc} that implements a simple star network as seen below. @sp 1 -@center @image{star,,,,png} +@center @image{figures/star,,,,png} In order to create a star network, we need to be able to instantiate some number (greater than one) of net devices on a node. In the name of simplicity @@ -506,7 +506,7 @@ configured with a lower bandwidth than the bus elements to provide a The following is a representation of the topology. @sp 1 -@center @image{dumbbell,,,,png} +@center @image{figures/dumbbell,,,,png} We have provided a file that constructs this dumbbell network and creates enough data flowing across the choke point that some packets will be dropped. diff --git a/doc/tutorial/packets.texi b/doc/tutorial/packets.texi new file mode 100644 index 000000000..cc00e0094 --- /dev/null +++ b/doc/tutorial/packets.texi @@ -0,0 +1,620 @@ +@node ns-3 Packets +@chapter ns-3 Packets + +The design of the Packet framework of @emph{ns} was heavily guided by a few +important use-cases: +@itemize @bullet +@item avoid changing the core of the simulator to introduce +new types of packet headers or trailers +@item maximize the ease of integration with real-world code +and systems +@item make it easy to support fragmentation, defragmentation, +and, concatenation which are important, especially in wireless systems. +@item make memory management of this object efficient +@item allow actual application data or dummy application bytes for +emulated applications +@end itemize + +@emph{ns} Packet objects contain a buffer of bytes: protocol headers and +trailers are serialized in this buffer of bytes using user-provided +serialization and deserialization routines. The content of this byte +buffer is expected to match bit-for-bit the content of a real packet on +a real network implementing the protocol of interest. + +Fragmentation and defragmentation are quite natural to implement within +this context: since we have a buffer of real bytes, we can split it in +multiple fragments and re-assemble these fragments. We expect that this +choice will make it really easy to wrap our Packet data structure within +Linux-style skb or BSD-style mbuf to integrate real-world kernel code in +the simulator. We also expect that performing a real-time plug of the +simulator to a real-world network will be easy. + +Because we understand that simulation developers often wish to store in +packet objects data which is not found in the real packets (such as +timestamps or any kind of similar in-band data), the @emph{ns} Packet class +can also store extra per-packet "Tags" which are 16 bytes blobs of data. +Any Packet can store any number of unique Tags, each of which is +uniquely identified by its C++ type. These tags make it easy to attach +per-model data to a packet without having to patch the main Packet +class or Packet facilities. + +Memory management of Packet objects is entirely automatic and extremely +efficient: memory for the application-level payload can be modelized by +a virtual buffer of zero-filled bytes for which memory is never allocated +unless explicitely requested by the user or unless the packet is fragmented. +Furthermore, copying, adding, and, removing headers or trailers to a packet +has been optimized to be virtually free through a technique known as +Copy On Write. + +Packets (messages) are fundamental objects in the simulator and +their design is important from a performance and resource management +perspective. There +are various ways to design the simulation packet, and tradeoffs +among the different approaches. In particular, there is a +tension between ease-of-use, performance, and safe interface +design. + +There are a few requirements on this object design: +@itemize @bullet +@item Creation, management, and deletion of this object +should be as simple as possible, while avoiding the +chance for memory leaks and/or heap corruption; +@item Packets should support serialization and deserialization +so that network emulation is supported; +@item Packets should support fragmentation and concatenation +(multiple packets in a data link frame), especially for wireless +support; +@item It should be natural for packets to carry actual application +data, or if there is only an emulated application and there is +no need to carry dummy bytes, smaller packets could be used with +just the headers and a record of the payload size, but not actual +application bytes, conveyed in the simulated packet. +@item Packets should facilitate BSD-like operations on mbufs, for +support of ported operating system stacks. +@item Additional side-information should be supported, such as +a tag for cross-layer information. +@end itemize + +@section Packet design overview + +Unlike @emph{ns-2}, in which Packet objects contain a buffer of C++ +structures corresponding to protocol headers, each network packet in +@emph{ns-3} contains a byte Buffer and a list of Tags: +@itemize @bullet +@item The byte buffer stores the serialized content of the chunks +added to a packet. The serialized representation of these chunks is +expected to match that of real network packets bit for bit +(although nothing forces you to do this) which means that the content +of a packet buffer is expected to be that of a real packet. +Packets can also be created with an arbitrary zero-filled payload +for which no real memory is allocated. +@item The list of tags stores an arbitrarily large set of arbitrary +user-provided data structures in the packet. Each Tag is uniquely +identified by its type; only one instance of each +type of data structure is allowed in a list of tags. These tags typically +contain per-packet cross-layer information or flow identifiers (i.e., +things that you wouldn't find in the bits on the wire). Each tag +stored in the tag list can be at most 16 bytes. +Trying to attach bigger data structures will trigger +crashes at runtime. The 16 byte limit is a modifiable compilation +constant. +@end itemize + +@float Figure,fig:packets +@caption{Implementation overview of Packet class.} +@image{figures/packet} +@end float + +Figure @ref{fig:packets} is a high-level overview of the Packet +implementation; more detail on the byte Buffer implementation +is provided later in Figure @ref{fig:buffer}. +In \nsthree, the Packet byte buffer is analogous to a Linux skbuff +or BSD mbuf; it is a serialized representation of the actual +data in the packet. The tag list is a container for extra +items useful for simulation convenience; if a Packet is converted +to an emulated packet and put over an actual network, the tags +are stripped off and the byte buffer is copied directly +into a real packet. + +The Packet class has value semantics: it can be freely copied around, +allocated on the stack, and passed to functions as arguments. Whenever +an instance is copied, the full underlying data is not copied; it +has ``copy-on-write'' (COW) semantics. Packet instances can be passed +by value to function arguments without any performance hit. + +The fundamental classes for adding to and removing from the byte +buffer are @code{class Header} and @code{class Trailer}. +Headers are more common but the below discussion also largely applies to +protocols using trailers. Every protocol header that needs to +be inserted and removed from a Packet instance should derive from +the abstract Header base class and implement the private pure +virtual methods listed below: +@itemize @bullet +@item @code{ns3::Header::SerializeTo()} +@item @code{ns3::Header::DeserializeFrom()} +@item @code{ns3::Header::GetSerializedSize()} +@item @code{ns3::Header::PrintTo()} +@end itemize +Basically, the first three functions are used to serialize and deserialize +protocol control information to/from a Buffer. For example, +one may define @code{class TCPHeader : public Header}. The +TCPHeader object will typically consist of some private data +(like a sequence number) and public interface access functions +(such as checking the bounds of an input). But the underlying +representation of the TCPHeader in a Packet Buffer is 20 serialized +bytes (plus TCP options). The TCPHeader::SerializeTo() function would +therefore be designed to write these 20 bytes properly into +the packet, in network byte order. The last function is used +to define how the Header object prints itself onto an output stream. + +Similarly, user-defined Tags can be appended to the packet. +Unlike Headers, Tags are not serialized into a contiguous buffer +but are stored in an array. By default, Tags are limited to 16 bytes in +size. Tags can be flexibly defined to be any type, but there +can only be one instance of any particular object type in +the Tags buffer at any time. The implementation makes use +of templates to generate the proper set of Add(), Remove(), +and Peek() functions for each Tag type. + +@section Packet interface + +The public member functions of a Packet object are as follows: + +@subsection Constructors +@verbatim + /** + * Create an empty packet with a new uid (as returned + * by getUid). + */ + Packet (); + /** + * Create a packet with a zero-filled payload. + * The memory necessary for the payload is not allocated: + * it will be allocated at any later point if you attempt + * to fragment this packet or to access the zero-filled + * bytes. The packet is allocated with a new uid (as + * returned by getUid). + * + * \param size the size of the zero-filled payload + */ + Packet (uint32_t size); +@end verbatim + +@subsection Adding and removing Buffer data +The below code is reproduced for Header class only; similar functions +exist for Trailers. +@verbatim + /** + * Add header to this packet. This method invokes the + * ns3::Header::serializeTo method to request the header to serialize + * itself in the packet buffer. + * + * \param header a reference to the header to add to this packet. + */ + void Add (Header const &header); + /** + * Deserialize header from this packet. This method invokes the + * ns3::Header::deserializeFrom method to request the header to deserialize + * itself from the packet buffer. This method does not remove + * the data from the buffer. It merely reads it. + * + * \param header a reference to the header to deserialize from the buffer + */ + void Peek (Header &header); + /** + * Remove a deserialized header from the internal buffer. + * This method removes the bytes read by Packet::peek from + * the packet buffer. + * + * \param header a reference to the header to remove from the internal buffer. + */ + void Remove (Header const &header); + /** + * Add trailer to this packet. This method invokes the + * ns3::Trailer::serializeTo method to request the trailer to serialize + * itself in the packet buffer. + * + * \param trailer a reference to the trailer to add to this packet. + */ +@end verbatim + +@subsection Adding and removing Tags +@verbatim + /** + * Attach a tag to this packet. The tag is fully copied + * in a packet-specific internal buffer. This operation + * is expected to be really fast. + * + * \param tag a pointer to the tag to attach to this packet. + */ + template + void AddTag (T const &tag); + /** + * Remove a tag from this packet. The data stored internally + * for this tag is copied in the input tag if an instance + * of this tag type is present in the internal buffer. If this + * tag type is not present, the input tag is not modified. + * + * This operation can be potentially slow and might trigger + * unexpectedly large memory allocations. It is thus + * usually a better idea to create a copy of this packet, + * and invoke removeAllTags on the copy to remove all + * tags rather than remove the tags one by one from a packet. + * + * \param tag a pointer to the tag to remove from this packet + * \returns true if an instance of this tag type is stored + * in this packet, false otherwise. + */ + template + bool RemoveTag (T &tag); + /** + * Copy a tag stored internally to the input tag. If no instance + * of this tag is present internally, the input tag is not modified. + * + * \param tag a pointer to the tag to read from this packet + * \returns true if an instance of this tag type is stored + * in this packet, false otherwise. + */ + template + bool PeekTag (T &tag) const; + /** + * Remove all the tags stored in this packet. This operation is + * much much faster than invoking removeTag n times. + */ + void RemoveAllTags (void); +@end verbatim + +@subsection Fragmentation +@verbatim + /** + * Create a new packet which contains a fragment of the original + * packet. The returned packet shares the same uid as this packet. + * + * \param start offset from start of packet to start of fragment to create + * \param length length of fragment to create + * \returns a fragment of the original packet + */ + Packet CreateFragment (uint32_t start, uint32_t length) const; + + /** + * Concatenate the input packet at the end of the current + * packet. This does not alter the uid of either packet. + * + * \param packet packet to concatenate + */ + void addAtEnd (Packet packet); + + /oncatenate the input packet at the end of the current + * packet. This does not alter the uid of either packet. + * + * \param packet packet to concatenate + */ + void AddAtEnd (Packet packet); + /** + * Concatenate the fragment of the input packet identified + * by the offset and size parameters at the end of the current + * packet. This does not alter the uid of either packet. + * + * \param packet to concatenate + * \param offset offset of fragment to copy from the start of the input packet + * \param size size of fragment of input packet to copy. + */ + void AddAtEnd (Packet packet, uint32_t offset, uint32_t size); + /** + * Remove size bytes from the end of the current packet + * It is safe to remove more bytes that what is present in + * the packet. + * + * \param size number of bytes from remove + */ + void RemoveAtEnd (uint32_t size); + /** + * Remove size bytes from the start of the current packet. + * It is safe to remove more bytes that what is present in + * the packet. + * + * \param size number of bytes from remove + */ + void RemoveAtStart (uint32_t size); +@end verbatim + +@subsection Miscellaneous +@verbatim + /** + * \returns the size in bytes of the packet (including the zero-filled + * initial payload) + */ + uint32_t GetSize (void) const; + /** + * If you try to change the content of the buffer + * returned by this method, you will die. + * + * \returns a pointer to the internal buffer of the packet. + */ + uint8_t const *PeekData (void) const; + /** + * A packet is allocated a new uid when it is created + * empty or with zero-filled payload. + * + * \returns an integer identifier which uniquely + * identifies this packet. + */ + uint32_t GetUid (void) const; +@end verbatim + +@section Using Headers +@emph{walk through an example of adding a UDP header} + +@section Using Tags +@emph{walk through an example of adding a flow ID} + +@section Using Fragmentation +@emph{walk through an example of link-layer fragmentation/reassembly} + +@section Sample program +The below sample program (from @code{ns3/samples/main-packet.cc}) illustrates +some use of the Packet, Header, and Tag classes. + +@verbatim +/* -*- Mode:C++; c-basic-offset:4; tab-width:4; indent-tabs-mode:nil -*- */ +#include "ns3/packet.h" +#include "ns3/header.h" +#include + +using namespace ns3; + +/* A sample Header implementation + */ +class MyHeader : public Header { +public: + MyHeader (); + virtual ~MyHeader (); + + void SetData (uint16_t data); + uint16_t GetData (void) const; +private: + virtual void PrintTo (std::ostream &os) const; + virtual void SerializeTo (Buffer::Iterator start) const; + virtual void DeserializeFrom (Buffer::Iterator start); + virtual uint32_t GetSerializedSize (void) const; + + uint16_t m_data; +}; + +MyHeader::MyHeader () +{} +MyHeader::~MyHeader () +{} +void +MyHeader::PrintTo (std::ostream &os) const +{ + os << "MyHeader data=" << m_data << std::endl; +} +uint32_t +MyHeader::GetSerializedSize (void) const +{ + return 2; +} +void +MyHeader::SerializeTo (Buffer::Iterator start) const +{ + // serialize in head of buffer + start.WriteHtonU16 (m_data); +} +void +MyHeader::DeserializeFrom (Buffer::Iterator start) +{ + // deserialize from head of buffer + m_data = start.ReadNtohU16 (); +} + +void +MyHeader::SetData (uint16_t data) +{ + m_data = data; +} +uint16_t +MyHeader::GetData (void) const +{ + return m_data; +} + +/* A sample Tag implementation + */ +struct MyTag { + uint16_t m_streamId; +}; + +static TagRegistration g_MyTagRegistration ("ns3::MyTag", 0); + + +static void +Receive (Packet p) +{ + MyHeader my; + p.Peek (my); + p.Remove (my); + std::cout << "received data=" << my.GetData () << std::endl; + struct MyTag myTag; + p.PeekTag (myTag); +} + + +int main (int argc, char *argv[]) +{ + Packet p; + MyHeader my; + my.SetData (2); + std::cout << "send data=2" << std::endl; + p.Add (my); + struct MyTag myTag; + myTag.m_streamId = 5; + p.AddTag (myTag); + Receive (p); + return 0; +} +@end verbatim + +@section Implementation details + +@subsection Private member variables + +A Packet object's interface provides access to some private +data: +@verbatim + Buffer m_buffer; + Tags m_tags; + uint32_t m_uid; + static uint32_t m_global_uid; +@end verbatim +Each Packet has a Buffer and a Tags object, and a 32-bit unique ID (m\_uid). +A static member variable keeps track of the UIDs allocated. Note +that real network packets do not have a UID; the UID is therefore an +instance of data that normally would be stored as a Tag in the packet. +However, it was felt that a UID is a special case that is so often +used in simulations that it would be more convenient to store it +in a member variable. + +@subsection Buffer implementation + +Class Buffer represents a buffer of bytes. Its size is +automatically adjusted to hold any data prepended +or appended by the user. Its implementation is optimized +to ensure that the number of buffer resizes is minimized, +by creating new Buffers of the maximum size ever used. +The correct maximum size is learned at runtime during use by +recording the maximum size of each packet. + +Authors of new Header or Trailer classes need to know the public +API of the Buffer class. (add summary here) + +The byte buffer is implemented as follows: +@verbatim + struct BufferData { + uint32_t m_count; + uint32_t m_size; + uint32_t m_initialStart; + uint32_t m_dirtyStart; + uint32_t m_dirtySize; + uint8_t m_data[1]; + }; + struct BufferData *m_data; + uint32_t m_zeroAreaSize; + uint32_t m_start; + uint32_t m_size; +@end verbatim + +@itemize @bullet +@item @code{BufferData::m_count}: reference count for BufferData structure +@item @code{BufferData::m_size}: size of data buffer stored in BufferData structure +@item @code{BufferData::m_initialStart}: offset from start of data buffer where data was first inserted +@item @code{BufferData::m_dirtyStart}: offset from start of buffer where every Buffer which holds a reference to this BufferData instance have written data so far +@item @code{BufferData::m_dirtySize}: size of area where data has been written so far +@item @code{BufferData::m_data}: pointer to data buffer +@item @code{Buffer::m_zeroAreaSize}: size of zero area which extends before @code{m_initialStart} +@item @code{Buffer::m_start}: offset from start of buffer to area used by this buffer +@item @code{Buffer::m_size}: size of area used by this Buffer in its BufferData structure +@end itemize + +@float Figure,fig:buffer +@caption{Implementation overview of a packet's byte Buffer.} +@image{figures/buffer,15cm} +@end float + +This data structure is summarized in Figure @ref{fig:buffer}. +Each Buffer holds a pointer to an instance of a BufferData. Most +Buffers should be able to share the same underlying BufferData and +thus simply increase the BufferData's reference count. If they have to +change the content of a BufferData inside the Dirty Area, and if the +reference count is not one, they first create a copy of the BufferData and +then complete their state-changing operation. + +@subsection Tags implementation +Tags are implemented by a single pointer which points to the start of a +linked list ofTagData data structures. Each TagData structure points +to the next TagData in the list (its next pointer contains zero to +indicate the end of the linked list). Each TagData contains an integer +unique id which identifies the type of the tag stored in the TagData. +@verbatim +struct TagData { + struct TagData *m_next; + uint32_t m_id; + uint32_t m_count; + uint8_t m_data[Tags::SIZE]; +}; +class Tags { + struct TagData *m_next; +}; +@end verbatim + +Adding a tag is a matter of inserting a new TagData at the head of +the linked list. Looking at a tag requires you to find the relevant +TagData in the linked list and copy its data into the user data +structure. Removing a tag and updating the content of a tag +requires a deep copy of the linked list before performing this operation. +On the other hand, copying a Packet and its tags is a matter of +copying the TagData head pointer and incrementing its reference count. + +Tags are found by the unique mapping betweent the Tag type and +its underlying id. This is why at most one instance of any Tag +can be stored in a packet. The mapping between Tag type and +underlying id is performed by a registration as follows: +@verbatim +/* A sample Tag implementation + */ +struct MyTag { + uint16_t m_streamId; +}; +@end verbatim + +@emph{add description of TagRegistration for printing} + +@subsection Memory management + +@emph{Describe free list.} + +@emph{Describe dataless vs. data-full packets.} + +@subsection Copy-on-write semantics +The current implementation of the byte buffers and tag list is based +on COW (Copy On Write). An introduction to COW can be found in Scott +Meyer's "More Effective C++", items 17 and 29). This design feature +and aspects of the public interface borrows from the packet design +of the Georgia Tech Network Simulator. +This implementation of COW uses a customized reference counting +smart pointer class. + +What COW means is that +copying packets without modifying them is very cheap (in terms of CPU +and memory usage) and modifying them can be also very cheap. What is +key for proper COW implementations is being +able to detect when a given modification of the state of a packet triggers +a full copy of the data prior to the modification: COW systems need +to detect when an operation is ``dirty'' and must therefore invoke +a true copy. + +Dirty operations: +@itemize @bullet +@item Packet::RemoveTag() +@item Packet::Add() +@item both versions of ns3::Packet::AddAtEnd() +@end itemize + +Non-dirty operations: +@itemize @bullet +@item Packet::AddTag() +@item Packet::RemoveAllTags() +@item Packet::PeekTag() +@item Packet::Peek() +@item Packet::Remove() +@item Packet::CreateFragment() +@item Packet::RemoveAtStart() +@item Packet::RemoveAtEnd() +@end itemize + +Dirty operations will always be slower than non-dirty operations, +sometimes by several orders of magnitude. However, even the +dirty operations have been optimized for common use-cases which +means that most of the time, these operations will not trigger +data copies and will thus be still very fast. + diff --git a/doc/tutorial/pp.png b/doc/tutorial/pp.png deleted file mode 100644 index 84b76d6df..000000000 Binary files a/doc/tutorial/pp.png and /dev/null differ diff --git a/doc/tutorial/star.png b/doc/tutorial/star.png deleted file mode 100644 index c580d0305..000000000 Binary files a/doc/tutorial/star.png and /dev/null differ diff --git a/doc/tutorial/tutorial.texi b/doc/tutorial/tutorial.texi index 737041b61..1af725da1 100644 --- a/doc/tutorial/tutorial.texi +++ b/doc/tutorial/tutorial.texi @@ -93,7 +93,9 @@ Part 3: Reconfiguring Existing ns-3 Scripts Part 4: Creating New or Revised Topologies * Helper Functions:: * Other-network-topologies:: -Part 5: Extending ns-3 +Part 5: Key ns-3 objects and systems +* ns-3 Packets:: +Part 6: Extending ns-3 * ns-3 Callbacks:: * ns-3 routing overview:: * Nonlinear-Thinking:: @@ -111,8 +113,9 @@ Part 5: Extending ns-3 @include attributes.texi @include statistics.texi @include helpers.texi +@include packets.texi @include callbacks.texi -@include output.texi +@c @include output.texi @include routing.texi @c @include other.texi @include troubleshoot.texi diff --git a/src/devices/point-to-point/point-to-point-channel.cc b/src/devices/point-to-point/point-to-point-channel.cc index 83a7abb46..94d3c6e40 100644 --- a/src/devices/point-to-point/point-to-point-channel.cc +++ b/src/devices/point-to-point/point-to-point-channel.cc @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2007 University of Washington + * Copyright (c) 2007, 2008 University of Washington * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -14,8 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Author: Craig Dowell */ #include "point-to-point-channel.h" diff --git a/src/devices/point-to-point/point-to-point-net-device.cc b/src/devices/point-to-point/point-to-point-net-device.cc index fb7d51110..c752e9023 100644 --- a/src/devices/point-to-point/point-to-point-net-device.cc +++ b/src/devices/point-to-point/point-to-point-net-device.cc @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2005,2006 INRIA + * Copyright (c) 2007, 2008 University of Washington * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -14,9 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Author: Craig Dowell - * Revised: George Riley */ #include "ns3/log.h" @@ -389,5 +386,4 @@ PointToPointNetDevice::SetReceiveCallback (NetDevice::ReceiveCallback cb) m_rxCallback = cb; } - } // namespace ns3 diff --git a/src/devices/point-to-point/point-to-point-net-device.h b/src/devices/point-to-point/point-to-point-net-device.h index ebfaf8239..b42ed4fe2 100644 --- a/src/devices/point-to-point/point-to-point-net-device.h +++ b/src/devices/point-to-point/point-to-point-net-device.h @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2007 University of Washington + * Copyright (c) 2007, 2008 University of Washington * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -14,8 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Author: Craig Dowell */ #ifndef POINT_TO_POINT_NET_DEVICE_H diff --git a/src/devices/point-to-point/point-to-point.h b/src/devices/point-to-point/point-to-point.h index 3ae8a24e3..f6799ceec 100644 --- a/src/devices/point-to-point/point-to-point.h +++ b/src/devices/point-to-point/point-to-point.h @@ -1,11 +1,45 @@ /** * \ingroup devices - * \defgroup PointToPoint Point-to-Point Models + * \defgroup PointToPoint Point-to-Point Model * - * \section Point-to-Point Models + * \section Point-to-Point Model * - * The set of Point-to-Point models provides an abstrated point-to-point link - * model, that simulates transmission delay (finite data rate) and - * propagation delay, and can also optionally include an error model - * (ns3::ErrorModel). + * The ns-3 point-to-point model is of a very simple point to point data link + * connecting exactly two ns3::PointToPointNetDevice devices over an + * ns3::PointToPointChannel. This can be viewed as equivalent to a full + * duplex RS-232 or RS-422 link with null modem and no handshaking. + * + * Data is encapsulated in the Point-to-Point Protocol (PPP -- RFC 1661), + * however the Link Control Protocol (LCP) and associated state machine is + * not implemented. The PPP link is assumed to be established and + * authenticated at all times. + * + * Data is not framed, therefore Address and Control fields will not be found. + * Since the data is not framed, there is no need to provide Flag Sequence and + * Control Escape octets, nor is a Frame Check Sequence appended. All that is + * required to implement non-framed PPP is to prepend the PPP protocol number + * for IP Version 4 which is the sixteen-bit number 0x21 (see + * http://www.iana.org/assignments/ppp-numbers). + * + * The ns3::PointToPointNetDevice provides following Attributes: + * + * - Address: The ns3::Mac48Address of the device (if desired); + * - DataRate: The data rate of the device; + * - TxQueue: The trasmit queue used by the device; + * - InterframeGap: The optional time to wait between "frames"; + * - Rx: A trace source for received packets; + * - Drop: A trace source for dropped packets. + * + * The ns3::PointToPointNetDevice supports the assignment of a "receive error + * model." This is an ns3::ErrorModel object that is used to simulate data + * corruption on the link. + * + * The point to point net devices are connected via an + * ns3::PointToPointChannel. This channel models two wires transmitting bits + * at the data rate specified by the source net device. There is no overhead + * beyond the eight bits per byte of the packet sent. That is, we do not + * model Flag Sequences, Frame Check Sequences nor do we "escape" any data. + * + * The ns3::PointToPointChannel does model a speed-of-light delay which can + * be accessed via the attribute "Delay." */ diff --git a/src/devices/point-to-point/ppp-header.cc b/src/devices/point-to-point/ppp-header.cc index 909e9f1b9..7cc62d585 100644 --- a/src/devices/point-to-point/ppp-header.cc +++ b/src/devices/point-to-point/ppp-header.cc @@ -55,7 +55,7 @@ PppHeader::GetInstanceTypeId (void) const void PppHeader::Print (std::ostream &os) const { - os << "Point-to-Point Protocol: IP (0x0021)" << std::endl; + os << "Point-to-Point Protocol: IP (0x0021)"; } uint32_t diff --git a/src/routing/global-routing/global-route-manager-impl.cc b/src/routing/global-routing/global-route-manager-impl.cc index 68d5dc43a..bf40994b0 100644 --- a/src/routing/global-routing/global-route-manager-impl.cc +++ b/src/routing/global-routing/global-route-manager-impl.cc @@ -1275,7 +1275,7 @@ GlobalRouteManagerImpl::SPFIntraAddRouter (SPFVertex* v) // the local side of the point-to-point links found on the node described by // the vertex . // - for (uint32_t j = 0; j < nLinkRecords; j += 2) + for (uint32_t j = 0; j < nLinkRecords; ++j) { // // We are only concerned about point-to-point links