Skip to content
Snippets Groups Projects
Commit 6be88efb authored by g0dil's avatar g0dil
Browse files

HowTos/NewPacket: Some updates

doclib: Find optimal graph rotation for large dot graphs
doclib: Break long pathnames in dot graphs
doclib: Use new DOT_GRAPH_MAX_NODES parameter in Doxyfile.global
doclib: Break lines in (textual) class inheritance lists
parent c7fb279a
No related branches found
No related tags found
No related merge requests found
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
both be omitted. both be omitted.
Another information we take from the RFC is, that the \a Protocol \a Type is used to define the Another information we take from the RFC is, that the \a Protocol \a Type is used to define the
type of payload which directly follows the GRE header. This value is the <a type of payload which directly follows the GRE header. This value is an <a
href="http://www.iana.org/assignments/ethernet-numbers">ETHERTYPE</a> value. To allow the packet href="http://www.iana.org/assignments/ethernet-numbers">ETHERTYPE</a> value. To allow the packet
library to automatically parse the GRE payload data, we need to tell the packet library which library to automatically parse the GRE payload data, we need to tell the packet library which
ETHERTYPE represents which packet type. This association already exists in form of the ETHERTYPE represents which packet type. This association already exists in form of the
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
\li The GRE packet header is a dynamically sized header. \li The GRE packet header is a dynamically sized header.
\li The GRE packet header utilizes the senf::EtherTypes registry for next-header selection \li The GRE packet header utilizes the senf::EtherTypes registry for next-header selection
\section howto_newpacket_impl Implementing the GRE Parser \section howto_newpacket_parser Implementing the GRE Parser
The next step in creating a new packet type is to implement the parser. The parser is The next step in creating a new packet type is to implement the parser. The parser is
responsible for turning a bunch of bytes into an interpreted header with specific fields. The responsible for turning a bunch of bytes into an interpreted header with specific fields. The
...@@ -97,9 +97,13 @@ ...@@ -97,9 +97,13 @@
SENF_PARSER_BITFIELD ( protocolType, 16, unsigned ); SENF_PARSER_BITFIELD ( protocolType, 16, unsigned );
\endcode \endcode
This is a direct transcript of the field definition above. However, we can optimize this a This is a direct transcript of the field definition above. There are quite a number of macros
little bit: Since the \a protocolType field is aligned on a byte boundary, instead of defining which may be used to define fields. All these macros are documented in '\ref
it as a bitfield, we can define it as a UInt16 field: packetparsermacros'.
This is a correct \c GREPacket header definition but we can optimize a little bit: Since the \a
protocolType field is aligned on a byte boundary, instead of defining it as a bitfield, we can
define it as a UInt16 field:
\code \code
SENF_PARSER_BITFIELD ( checksumPresent, 1, bool ); SENF_PARSER_BITFIELD ( checksumPresent, 1, bool );
...@@ -109,27 +113,28 @@ ...@@ -109,27 +113,28 @@
SENF_PARSER_FIELD ( protocolType, senf::UInt16Parser ); SENF_PARSER_FIELD ( protocolType, senf::UInt16Parser );
\endcode \endcode
There are quite a number of such \c SENF_PARSER_ macros. They are all listed in \ref Whereas \ref SENF_PARSER_BITFIELD can define only bit-fields, \ref SENF_PARSER_FIELD can define
packetparsermacros. The basic field types (like senf::UInt16Parser) are listed in \ref parseint. almost arbitrary field types. The type is specified by passing the name of another parser to
\ref SENF_PARSER_FIELD.
What happens in the above macros? Most of the macros define an accessor for a specific field: \a
checksumPresent() or \a protocolType(). They also manage a <em>current Offset</em>. This value
is advanced according to the field size whenever a new field is defined (and since this parser
is defined as a dynamically sized parser, this offset is not a constant, it is an expression
which calculates the offset of a field depending on the preceding data).
It is important to understand, that the accessors do \e not return the parsed field value. They It is important to understand, that the accessors do \e not return the parsed field value. They
return another \e parser which is used to further interpret the value. This is the inherent return another \e parser which is used to further interpret the value. This is the inherent
recursive nature of the SENF packet parsers. This allows to define wildly complex header formats recursive nature of the SENF packet parsers. This allows to define wildly complex header formats
if needed. Of course, at some point we need the real value. This is, what the so called if needed. Of course, at some point we need the real value. This is, what the so called
<em>value parsers</em> do: They interpret some bytes or bits and return the value of that field <em>value parsers</em> do: They interpret some bytes or bits and return the value of that field
(not a parser). Examples are the bitfield parsers returnd by the accessors generated by (not a parser). Examples are the bitfield parsers returned by the accessors generated by
SENF_PARSER_BITFIELD (like senf::UIntFieldParser) or the senf::UInt16Parser. SENF_PARSER_BITFIELD (like senf::UIntFieldParser) or the senf::UInt16Parser.
What happens in the above macros? Most of the macros define an accessor for a specific field: \a
checksumPresent() or \a protocolType(). They also manage a <em>current Offset</em>. This value
is advanced according to the field size whenever a new field is defined (and since this parser
is defined as a dynamically sized parser, this offset is not a constant, it is an expression
which calculates the offset of a field depending on the preceding data).
We now come to the optional fields. Since there are two fields which need to be disabled/enabled We now come to the optional fields. Since there are two fields which need to be disabled/enabled
together, we first need to define an additional sub-parser which parses those two fields. After together, we first need to define an additional sub-parser which combines those two
this parser is defined, we can use \ref SENF_PARSER_VARIANT() to add this parser as an optional fields. After this parser is defined, we can use \ref SENF_PARSER_VARIANT() to add this parser
parser to the GRE header. as an optional parser to the GRE header.
\code \code
struct GREParser_OptFields : public senf::PacketParser struct GREParser_OptFields : public senf::PacketParser
...@@ -159,32 +164,35 @@ ...@@ -159,32 +164,35 @@
(GREParser_OptFields) ); (GREParser_OptFields) );
\endcode \endcode
How does this work? For a variant parser, two things need to be specified: A selector and a list For a variant parser, two things need to be specified: A selector and a list of variant
of variant parsers. The selector is another parser field which is used to decide, which variant parsers. The selector is another parser field which is used to decide, which variant to
to choose. In this simple case, the field must be an unsigned integer (more precisely a value choose. In this simple case, the field must be an unsigned integer (more precisely a value
parser which returns a value which is implicitly convertible to \c unsigned). This value is used parser which returns a value which is implicitly convertible to \c unsigned). This value is used
as index into the list of variant types. So in our case, 0 is associated with as index into the list of variant types. So in our case, 0 is associated with
senf::VoidPacketParser whereas 1 is associated with \c GREParser_OptFields. senf::VoidPacketParser whereas 1 is associated with \c
GREParser_OptFields. (senf::VoidPacketParser is a special empty parser which is used in a
Variant to denote cases in which the variant parser should not parse anything)
This parser will work, it is however not very safe and not very usable. If \a p is a GREParser This parser will work, it is however not very safe and not very usable. If \a p is a GREParser
instance, than we access the fields via: instance, than we access the fields via:
\code \code
p.checksumPresent() p.checksumPresent() = true;
p.version() p.version() = 4u;
p.protocolType() p.protocolType() = 0x86dd;
p.optionalFields().get<1>().checksum() p.optionalFields().get<1>().checksum() = 12345u;
\endcode \endcode
There are two problems here: There are two problems here:
\li accessing the checksum field is not straight forward \li accessing the checksum field is quite unwieldy
\li changing the checksumPresent() value will break the parser \li changing the checksumPresent() value will break the parser
The reason for the second problem lies in the fact, that the variant parser needs to be informed The reason for the second problem lies in the fact, that the variant parser needs to be informed
whenever the selector (here \a checksumPresent) is changed since the variant parser must ensure, whenever the selector (here \a checksumPresent) is changed since the variant parser must ensure,
that the header data stays consistent. In this example, whenever the checksumPresent field is that the header data stays consistent. In this example, whenever the checksumPresent field is
enabled, the variant parser needs to insert additional 4 bytes of data and remove those bytes, enabled, the variant parser needs to insert additional 4 bytes of data and remove those bytes,
when the checksumPresent field is disabled. We therefore make the checksumPresent field when the checksumPresent field is disabled.
read-only:
To fix this, we make the checksumPresent field read-only:
\code \code
SENF_PARSER_BITFIELD_RO ( checksumPresent, 1, bool ); SENF_PARSER_BITFIELD_RO ( checksumPresent, 1, bool );
...@@ -199,18 +207,21 @@ ...@@ -199,18 +207,21 @@
The first statements switches to the first variant and therefore in this case disables the The first statements switches to the first variant and therefore in this case disables the
checksum field. The second statement will switch to the second variant and enable the checksum checksum field. The second statement will switch to the second variant and enable the checksum
field. This again is not very usable. So we complete the parser by providing simple additional field.
members which access the fields in a more readable way. While doing this, we also mark the
variant as a private field so it is not directly accessible any more. Here the final \c This again is not very usable. So we complete the parser by providing simple additional members
GREParser which wrap these complicated calls. While doing this, we also mark the variant as a private
field so it is not directly accessible any more (since we now have the additional helpers which
are used to access the variant, we don't want anyone to mess around with it directly). Here the
final \c GREParser
\code \code
struct GREParser_OptFields : public senf::PacketParser struct GREParser_OptFields : public senf::PacketParser
{ {
# include SENF_FIXED_PARSER() # include SENF_FIXED_PARSER()
SENF_PARSER_FIELD ( checksum, senf::UInt16Parser ); SENF_PARSER_FIELD ( checksum, senf::UInt16Parser );
SENF_PARSER_SKIP ( 2 ); SENF_PARSER_SKIP ( 2 );
SENF_PARSER_FINALIZE(GREParser_OptFields); SENF_PARSER_FINALIZE(GREParser_OptFields);
}; };
...@@ -239,6 +250,20 @@ ...@@ -239,6 +250,20 @@
SENF_PARSER_FINALIZE(GREParser); SENF_PARSER_FINALIZE(GREParser);
}; };
\endcode \endcode
Above code has one other twist we need to discuss: the \a checksum_t typedef. This is added as a
convenience to the user of this parser. The \c SENF_PARSER_* macros which define a field all
define some additional symbols providing further information about the field. Of these
additional symbols, the most important is <em>field</em><code>_t</code>, which is the (parser)
type returned by the field. This helps to work with a parser in more complex situations
(e.g. when using collection parsers) since it allows to access the parser type without exact
knowledge of this type (which may become quite complex if templates are involved) as long as the
field name is known. Since we provide an accessor for the \a checksum field, we also provide the
\a checksum_t typedef for this accessor.
The \c GREParser is now simple and safe to use. The only responsibility of the user now is to
only access \a checksum() if the \a checksumPresent() field is set. Otherwise, the behavior is
undefined (in debug builds, the parser will terminate the application with an assert).
*/ */
......
...@@ -73,9 +73,8 @@ TEMPLATE_RELATIONS = NO ...@@ -73,9 +73,8 @@ TEMPLATE_RELATIONS = NO
INCLUDE_GRAPH = YES INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES INCLUDED_BY_GRAPH = YES
GROUP_GRAPHS = NO GROUP_GRAPHS = NO
MAX_DOT_GRAPH_WIDTH = 800
MAX_DOT_GRAPH_HEIGHT = 1600
MAX_DOT_GRAPH_DEPTH = 3 MAX_DOT_GRAPH_DEPTH = 3
DOT_GRAPH_MAX_NODES = 10
DOT_MULTI_TARGETS = YES DOT_MULTI_TARGETS = YES
DOT_CLEANUP = NO DOT_CLEANUP = NO
DOT_PATH = "$(TOPDIR)/doclib" DOT_PATH = "$(TOPDIR)/doclib"
...@@ -85,4 +84,4 @@ DOT_PATH = "$(TOPDIR)/doclib" ...@@ -85,4 +84,4 @@ DOT_PATH = "$(TOPDIR)/doclib"
# Local Variables: # Local Variables:
# mode: indented-text # mode: indented-text
# indent-tabs-mode: nil # indent-tabs-mode: nil
# End: # End:
\ No newline at end of file
...@@ -2,4 +2,37 @@ ...@@ -2,4 +2,37 @@
topdir="`dirname "$0"`"; topdir="`cd "$topdir/.."; pwd`" topdir="`dirname "$0"`"; topdir="`cd "$topdir/.."; pwd`"
"$topdir/doclib/dot-munge.pl" "$1" "$topdir/doclib/dot-munge.pl" "$1"
exec dot "$@"
case "$2" in
-Tpng:gd)
first="$1"; shift; shift
set -- "$first" "-Tpng" "$@"
;;
*)
esac
set -e
dot "$@"
if [ -r "$4" ]; then
size="`pngtopnm "$4" | sed -n -e '2p'`"
width_a="${size% *}"
height_a="${size#* }"
if [ $width_a -gt 800 ]; then
for file in "${1%.dot}".*; do
cp "$file" "$file.a"
done
sed -e 's/rankdir=LR/rankdir=TB/' -e t -e 's/rankdir=TB/rankdir=LR/' "$1.a" > "$1"
dot "$@"
size="`pngtopnm "$4" | sed -n -e '2p'`"
width_b="${size% *}"
height_b="${size#* }"
if [ $width_a -lt $width_b ]; then
for file in "${1%.dot}".*.a; do
mv "$file" "${file%.a}"
done
else
rm "${1%.dot}".*.a
fi
fi
fi
#!/usr/bin/perl -i -n #!/usr/bin/perl -i -n
# Reduce font size # Reduce fontsize and change font
s/fontsize=10/fontsize=8/g; s/fontsize=10/fontsize=8/g;
##s/fontname="FreeSans.ttf"/fontname="Bitstream Vera Sans Mono"/g;
s/fontname="FreeSans.ttf"/fontname="Verdana"/g; s/fontname="FreeSans.ttf"/fontname="Verdana"/g;
##s/fontname="FreeSans.ttf"/fontname="Lucida Sans Typewriter"/g;
# Wrap long labels (templates) # Wrap long labels (templates and pathnames)
if (/label=\"([^"]*)\"/) { #"])){ # To make emacs happy ... if (/label=\"([^"]*)\"/) { #"])){ # To make emacs happy ...
$pre=$`; $pre=$`;
$post=$'; #'; # To make emacs happy ... $post=$'; #'; # To make emacs happy ...
$label=$1; $label=$1;
# Break at each komma # Break at each komma or /
$label=~s/,/,\\r\\ \\ \\ \\ \\ \\ \\ \\ /g; $label=~s{[,/]}{$&\\r\\ \\ \\ \\ \\ \\ \\ \\ }g;
# If more than one '<' is in the label, break after each '<' # If more than one '<' is in the label, break after each '<'
if (($label=~tr/</</)>1) { if (($label=~tr/</</)>1) {
...@@ -22,10 +20,17 @@ if (/label=\"([^"]*)\"/) { #"])){ # To make ema ...@@ -22,10 +20,17 @@ if (/label=\"([^"]*)\"/) { #"])){ # To make ema
# If at least one break is in there ... # If at least one break is in there ...
if ($label=~/\\r/) { if ($label=~/\\r/) {
# If it's a pathname, make all but the last line flush left
# Otherwise only make first line flush left
if ($label=~m{/}) {
$label=~s/\\r(\\ )*/\\ \\ \\ \\ \\ \\ \\ \\ \\l/g;
# Re-add blanks before last line
$label=~s/^.*\\l/$&\\ \\ \\ \\ \\ \\ \\ \\ /;
} else {
$label=~s/\\r/\\ \\ \\ \\ \\ \\ \\ \\ \\l/;
}
# Make last line flush right # Make last line flush right
$label.="\\r"; $label.="\\r";
# and first line flush left
$label=~s/\\r/\\ \\ \\ \\ \\ \\ \\ \\ \\l/;
} }
print "${pre}label=\"${label}\"${post}"; print "${pre}label=\"${label}\"${post}";
} else { } else {
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
<xsl:param name="topdir" select="''"/> <xsl:param name="topdir" select="''"/>
<xsl:template match="*"> <xsl:template match="*" name="copy">
<xsl:copy> <xsl:copy>
<xsl:call-template name="copy-attributes"/> <xsl:call-template name="copy-attributes"/>
<xsl:apply-templates/> <xsl:apply-templates/>
...@@ -33,15 +33,12 @@ ...@@ -33,15 +33,12 @@
</xsl:for-each> </xsl:for-each>
</xsl:template> </xsl:template>
<!-- Remove the automatically inserted search form (we build our own) --> <!-- Replace @TOPDIR@ with relative top directory path -->
<xsl:template match="li[form]">
</xsl:template>
<!-- Replace @TOPDIR@ with relative top directory path -->
<xsl:template match="@*[contains(current(),'@TOPDIR@')]"> <xsl:template match="@*[contains(current(),'@TOPDIR@')]">
<xsl:value-of select="substring-before(current(),'@TOPDIR')"/> <xsl:value-of select="substring-before(current(),'@TOPDIR@')"/>
<xsl:value-of select="$topdir"/> <xsl:value-of select="$topdir"/>
<xsl:value-of select="substring-after(current(),'@TOPDIR')"/> <xsl:value-of select="substring-after(current(),'@TOPDIR@')"/>
</xsl:template> </xsl:template>
<!-- Add 'class' attribute to some special paragraphs/lists --> <!-- Add 'class' attribute to some special paragraphs/lists -->
...@@ -55,6 +52,43 @@ ...@@ -55,6 +52,43 @@
</xsl:copy> </xsl:copy>
</xsl:template> </xsl:template>
<!-- Add '<br/>' tag after every ', ' -->
<!-- This code is not very robust, it works with the doxygen output though -->
<xsl:template name="break-comma">
<xsl:copy>
<xsl:call-template name="copy-attributes"/>
<xsl:attribute name="class">commalist</xsl:attribute>
<xsl:apply-templates mode="break-comma"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()[1]" mode="break-comma">
<xsl:value-of select="current()"/><br/>
</xsl:template>
<xsl:template match="*" mode="break-comma">
<xsl:call-template name="copy"/>
</xsl:template>
<xsl:template match="text()[contains(current(),' and') or contains(current(),'and ')]" mode="break-comma" priority="1">
<xsl:value-of select="substring-before(current(),'and')"/>
<br/>
<xsl:value-of select="substring-after(current(),'and')"/>
</xsl:template>
<xsl:template match="text()[contains(current(),',')]" mode="break-comma">
<xsl:value-of select="substring-before(current(),',')"/>
<xsl:text>,</xsl:text><br/>
<xsl:value-of select="substring-after(current(),',')"/>
</xsl:template>
<!-- ====================================================================== -->
<!-- Remove the automatically inserted search form (we build our own) -->
<xsl:template match="li[form]">
</xsl:template>
<xsl:template match="dl[dt/b/a/text()='Bug:']"> <xsl:template match="dl[dt/b/a/text()='Bug:']">
<xsl:call-template name="add-class"> <xsl:call-template name="add-class">
<xsl:with-param name="class">xref-bug</xsl:with-param> <xsl:with-param name="class">xref-bug</xsl:with-param>
...@@ -157,6 +191,14 @@ ...@@ -157,6 +191,14 @@
</xsl:call-template> </xsl:call-template>
</xsl:template> </xsl:template>
<xsl:template match="p[starts-with(text(),'Inherited by ')]">
<xsl:call-template name="break-comma"/>
</xsl:template>
<xsl:template match="p[starts-with(text(),'Inherits ')]">
<xsl:call-template name="break-comma"/>
</xsl:template>
<!-- Remove external items from the namespace index --> <!-- Remove external items from the namespace index -->
<xsl:template match="div[@id='content2']/table[contains(preceding-sibling::h1/text(),'Namespace Reference')]/tr[td[@class='memItemRight']/a[1][@class='elRef'][@doxygen]]"> <xsl:template match="div[@id='content2']/table[contains(preceding-sibling::h1/text(),'Namespace Reference')]/tr[td[@class='memItemRight']/a[1][@class='elRef'][@doxygen]]">
</xsl:template> </xsl:template>
......
...@@ -480,7 +480,13 @@ div.toc li { ...@@ -480,7 +480,13 @@ div.toc li {
div.toc div { div.toc div {
margin: 10px 0px; margin: 10px 0px;
font-weight: bold; font-weight: bold;
font-size: 120% font-size: 120%;
}
p.commalist {
white-space: nowrap;
margin-left: 4em;
text-indent: -4em;
} }
/* /*
......
...@@ -34,6 +34,7 @@ cc ...@@ -34,6 +34,7 @@ cc
cd cd
cerr cerr
cfi cfi
checksumPresent
CIDR CIDR
ClientSocketHandle ClientSocketHandle
CloneSource CloneSource
...@@ -58,12 +59,15 @@ defaultInit ...@@ -58,12 +59,15 @@ defaultInit
defgroup defgroup
deque deque
dil dil
disableChecksum
dl dl
DNS DNS
dontinclude dontinclude
dox
DSMCCSection DSMCCSection
dt dt
ElementParser ElementParser
enableChecksum
endcode endcode
enum enum
eof eof
...@@ -109,6 +113,7 @@ FroblizerArea ...@@ -109,6 +113,7 @@ FroblizerArea
GlobalScope GlobalScope
GRE GRE
GREPacket GREPacket
GREParser
hangup hangup
HangupException HangupException
hh hh
...@@ -121,7 +126,7 @@ href ...@@ -121,7 +126,7 @@ href
htm htm
html html
http http
IANA iana
IdleEvent IdleEvent
ietf ietf
ih ih
...@@ -186,6 +191,8 @@ onRequest ...@@ -186,6 +191,8 @@ onRequest
onThrottle onThrottle
onUnthrottle onUnthrottle
Ooops Ooops
OptFields
optionalFields
org org
os os
ostream ostream
...@@ -242,6 +249,7 @@ prev ...@@ -242,6 +249,7 @@ prev
PriorityJoin PriorityJoin
protocolbundle protocolbundle
protocolbundles protocolbundles
protocolType
QueueingDiscipline QueueingDiscipline
queueSize queueSize
RateFilter RateFilter
...@@ -317,6 +325,7 @@ udpReader ...@@ -317,6 +325,7 @@ udpReader
udpWriter udpWriter
UInt UInt
UIntField UIntField
UIntFieldParser
Unhandled Unhandled
unicast unicast
unthrottle unthrottle
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment