merged DTLS support

git-svn-id: file:///Users/braun/svn/vermont/branches/vermont/dtls-merge@2394 aef3b71b-58ee-0310-9ba9-8811b9f0742f
master
danielmentz 2010-05-27 15:44:33 +00:00
parent 0934e21030
commit 46f10287be
67 changed files with 8192 additions and 1880 deletions

View File

@ -308,6 +308,39 @@ IF (CONNECTION_FILTER)
ADD_DEFINITIONS(-DHAVE_CONNECTION_FILTER)
ENDIF(CONNECTION_FILTER)
### openssl
OPTION(SUPPORT_DTLS "Enables/Disables encryption support for IPFIX messages." OFF)
IF (SUPPORT_DTLS)
FIND_PACKAGE(OpenSSL)
IF (NOT OPENSSL_FOUND)
MESSAGE(FATAL_ERROR "Could not find openssl. Please install the library or turn off SUPPORT_DTLS")
ENDIF (NOT OPENSSL_FOUND)
INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(vermont ${OPENSSL_LIBRARIES})
ADD_DEFINITIONS(-DSUPPORT_DTLS)
## check wether there is a libcrypto
# FIND_PACKAGE(Crypto)
# IF (CRYPTO_FOUND)
# TARGET_LINK_LIBRARIES(vermont ${CRYPTO_LIBRARIES})
# ENDIF (CRYPTO_FOUND)
ELSE(SUPPORT_DTLS)
REMOVE_DEFINITIONS(-DSUPPORT_DLTS)
ENDIF(SUPPORT_DTLS)
OPTION(SUPPORT_DTLS_OVER_SCTP "Enables/Disables DTLS on SCTP streams." OFF)
IF (SUPPORT_DTLS_OVER_SCTP)
IF (SUPPORT_SCTP AND SUPPORT_DTLS)
ADD_DEFINITIONS(-DSUPPORT_DTLS_OVER_SCTP)
ELSE (SUPPORT_SCTP AND SUPPORT_DTLS)
MESSAGE(FATAL_ERROR "SUPPORT_DTLS_OVER_SCTP can only be enabled together with SUPPORT_SCTP and SUPPORT_DTLS")
REMOVE_DEFINITIONS(-DSUPPORT_DTLS_OVER_SCTP)
ENDIF(SUPPORT_SCTP AND SUPPORT_DTLS)
ELSE (SUPPORT_DTLS_OVER_SCTP)
REMOVE_DEFINITIONS(-DSUPPORT_DTLS_OVER_SCTP)
ENDIF (SUPPORT_DTLS_OVER_SCTP)
### tools
OPTION(WITH_TOOLS "Build misc tools." ON)

View File

@ -11,7 +11,7 @@ Optional:
- libmysqlclient-dev (for MySQL support)
- libgsl-dev (for connection-based sampling with Bloom filters)
Note: for high efficiency the PCAP-MMAP modification is suggested.
Note: for high efficiency, the PCAP-MMAP modification is suggested.
See <http://public.lanl.gov/cpw/>

77
configs/dtls_exporter.xml Normal file
View File

@ -0,0 +1,77 @@
<ipfixConfig>
<sensorManager id="99">
<checkinterval>2</checkinterval>
</sensorManager>
<observer id="1">
<filename>../sample_data/sample1.cap</filename>
<pcap_filter>ip</pcap_filter>
<!-- offlineSpeed>-1</offlineSpeed -->
<next>2</next>
</observer>
<packetQueue id="2">
<maxSize>10</maxSize>
<next>6</next>
</packetQueue>
<packetAggregator id="6">
<rule>
<templateId>998</templateId>
<flowKey>
<ieName>sourceIPv4Address</ieName>
</flowKey>
<flowKey>
<ieName>destinationIPv4Address</ieName>
</flowKey>
<flowKey>
<ieName>protocolIdentifier</ieName>
</flowKey>
<flowKey>
<ieName>sourceTransportPort</ieName>
</flowKey>
<flowKey>
<ieName>destinationTransportPort</ieName>
</flowKey>
<nonFlowKey>
<ieName>flowStartMilliSeconds</ieName>
</nonFlowKey>
<nonFlowKey>
<ieName>flowEndMilliSeconds</ieName>
</nonFlowKey>
<nonFlowKey>
<ieName>octetDeltaCount</ieName>
</nonFlowKey>
<nonFlowKey>
<ieName>packetDeltaCount</ieName>
</nonFlowKey>
<nonFlowKey>
<ieName>tcpControlBits</ieName>
</nonFlowKey>
</rule>
<expiration>
<inactiveTimeout unit="sec">1</inactiveTimeout>
<activeTimeout unit="sec">1</activeTimeout>
</expiration>
<pollInterval unit="msec">1000</pollInterval>
<next>7</next>
</packetAggregator>
<ipfixQueue id="7">
<entries>1</entries>
<next>8</next>
</ipfixQueue>
<ipfixExporter id="8">
<cert>configs/example_certs/exporter_cert.pem</cert>
<key>configs/example_certs/exporter_key.pem</key>
<CAfile>configs/example_certs/vermontCA.pem</CAfile>
<CApath>/etc/ssl/certs</CApath>
<dtlsMaxConnectionLifetime unit="sec">10</dtlsMaxConnectionLifetime>
<collector>
<ipAddressType>4</ipAddressType>
<ipAddress>127.0.0.1</ipAddress>
<transportProtocol>DTLS_OVER_UDP</transportProtocol>
<peerFqdn>collector.example.com</peerFqdn>
</collector>
</ipfixExporter>
</ipfixConfig>

18
configs/dtls_printer.xml Normal file
View File

@ -0,0 +1,18 @@
<ipfixConfig>
<ipfixCollector id="1">
<cert>configs/example_certs/collector_cert.pem</cert>
<key>configs/example_certs/collector_key.pem</key>
<CAfile>configs/example_certs/vermontCA.pem</CAfile>
<CApath>/etc/ssl/certs</CApath>
<listener>
<!-- transportProtocol>DTLS_OVER_SCTP</transportProtocol -->
<transportProtocol>DTLS_OVER_UDP</transportProtocol>
<!-- peerFqdn>ex352.example.com</peerFqdn -->
<!-- peerFqdn>exporter.example.com</peerFqdn -->
</listener>
<next>2</next>
</ipfixCollector>
<ipfixPrinter id="2">
</ipfixPrinter>
</ipfixConfig>

View File

@ -171,7 +171,6 @@
</ipfixPacketRestrictions>
<udpTemplateManagement>
<templateRefreshTimeout>10</templateRefreshTimeout>
<templateRefreshRate>100</templateRefreshRate>
</udpTemplateManagement>
-->
<collector>

View File

@ -0,0 +1,57 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 3 (0x3)
Signature Algorithm: sha1WithRSAEncryption
Issuer: CN=Vermont example CA
Validity
Not Before: Mar 3 17:34:16 2009 GMT
Not After : Feb 26 17:34:16 2029 GMT
Subject: CN=Collector
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public Key: (1024 bit)
Modulus (1024 bit):
00:c8:a8:bc:ed:e1:4a:1c:e6:7c:db:f2:41:26:99:
0c:97:9f:52:4f:4f:ec:06:35:2f:32:ec:3c:88:13:
b2:d3:88:83:00:d9:5b:a2:58:be:25:3c:16:67:92:
61:1e:7e:3d:9a:7a:01:7d:ca:71:76:f3:96:74:80:
ec:78:3c:32:26:13:3a:d8:02:60:23:2d:b5:e5:88:
93:93:86:f9:cb:c4:f4:7f:40:53:14:2a:9a:65:f5:
9e:6f:7d:52:7f:ae:f2:b5:2f:9a:54:23:fc:fa:ed:
57:4a:23:c7:f9:87:e6:1f:e4:d3:47:45:c6:4a:2e:
94:38:ae:51:c8:06:7d:4f:05
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Subject Key Identifier:
48:B1:AF:25:4D:6C:97:56:64:84:2F:3F:F7:E6:CD:26:C9:95:0F:E9
X509v3 Authority Key Identifier:
keyid:F9:79:19:E7:91:26:27:24:EC:78:65:8C:BB:CD:10:8F:A2:1A:DC:05
X509v3 Subject Alternative Name:
DNS:collector.example.com
Signature Algorithm: sha1WithRSAEncryption
5e:63:1a:f2:ff:c0:dd:b6:3f:ef:f0:14:3d:6c:67:95:e1:ab:
1a:ef:e8:16:fc:0d:f6:4f:2e:7d:05:2f:02:ff:27:d0:f0:0a:
dd:fe:9a:f7:d3:bb:43:2c:9c:f6:50:6f:ec:00:03:b0:f4:86:
77:3c:0b:86:fb:09:c7:76:75:0e:19:44:21:11:c7:1b:5b:d8:
1c:59:ae:49:79:e0:5e:b1:6c:34:c9:b1:a1:61:70:6a:32:05:
b8:c5:60:01:a5:ab:36:1b:4f:41:32:a0:90:e9:7c:ea:3c:45:
0f:47:7a:c4:cb:b7:8a:5f:51:4f:d2:c8:14:e8:bc:e3:99:2b:
3a:2a
-----BEGIN CERTIFICATE-----
MIICFjCCAX+gAwIBAgIBAzANBgkqhkiG9w0BAQUFADAdMRswGQYDVQQDExJWZXJt
b250IGV4YW1wbGUgQ0EwHhcNMDkwMzAzMTczNDE2WhcNMjkwMjI2MTczNDE2WjAU
MRIwEAYDVQQDEwlDb2xsZWN0b3IwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB
AMiovO3hShzmfNvyQSaZDJefUk9P7AY1LzLsPIgTstOIgwDZW6JYviU8FmeSYR5+
PZp6AX3KcXbzlnSA7Hg8MiYTOtgCYCMtteWIk5OG+cvE9H9AUxQqmmX1nm99Un+u
8rUvmlQj/PrtV0ojx/mH5h/k00dFxkoulDiuUcgGfU8FAgMBAAGjbzBtMAkGA1Ud
EwQCMAAwHQYDVR0OBBYEFEixryVNbJdWZIQvP/fmzSbJlQ/pMB8GA1UdIwQYMBaA
FPl5GeeRJick7HhljLvNEI+iGtwFMCAGA1UdEQQZMBeCFWNvbGxlY3Rvci5leGFt
cGxlLmNvbTANBgkqhkiG9w0BAQUFAAOBgQBeYxry/8Ddtj/v8BQ9bGeV4asa7+gW
/A32Ty59BS8C/yfQ8Ard/pr307tDLJz2UG/sAAOw9IZ3PAuG+wnHdnUOGUQhEccb
W9gcWa5JeeBesWw0ybGhYXBqMgW4xWABpas2G09BMqCQ6XzqPEUPR3rEy7eKX1FP
0sgU6LzjmSs6Kg==
-----END CERTIFICATE-----

View File

@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDIqLzt4Uoc5nzb8kEmmQyXn1JPT+wGNS8y7DyIE7LTiIMA2Vui
WL4lPBZnkmEefj2aegF9ynF285Z0gOx4PDImEzrYAmAjLbXliJOThvnLxPR/QFMU
Kppl9Z5vfVJ/rvK1L5pUI/z67VdKI8f5h+Yf5NNHRcZKLpQ4rlHIBn1PBQIDAQAB
AoGAIGWF404tXg8kd4pcVHP/YXd6nY2EyNfLYAheGYY8qARxSjDNp592S6Kw51Xt
0jOFlKxAE2Qc/5yCXRr3ks39NnlJkdxYPehjobIemFlr/rW85S5Qds9gjV0VBbnd
ouozSi9Onk/yOkB8jd71aKuKvzy07IOK9kW/RrVcqu8G5IECQQDp30a+LlPftiwo
7KC9o08OapTOROcnvRPOql3UrTzQYZbaSSUT589UMS6FyAxayAdVb92VwLsubyBc
3J1QGdLlAkEA26T+XFBWPsq2WvnBERb/g5Ik0kKy26ME1gObOvbG8zlO2kUF95vz
t19LaakgUv7qMV2HPeK5J3KHq05mgQUpoQJAIAyV8Df/DHg1gwIyYOqBSfN3IvE0
UDDMBxU3uI5o+BF3j8BYUWsB8YKv4mtwrfwdbSrgTcZUoF9gKvmcoT54tQJAWa2m
BP7wF7cgeUib4WRocsnKquZ8rFyE7vSN/qcfV9NANLIV26EbAvWvjrZ08i4OZJVx
UH0vZ8HFTtY119vJwQJBAKlXaAAeM3wPYfU+kd0ogQJssCMHLCxTw17RaN21Io9p
AHM+elYKJgkInoCynGV+s/Ajs5FB62rLJs3GBKi+clY=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,57 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 2 (0x2)
Signature Algorithm: sha1WithRSAEncryption
Issuer: CN=Vermont example CA
Validity
Not Before: Mar 3 17:32:37 2009 GMT
Not After : Feb 26 17:32:37 2029 GMT
Subject: CN=Exporter
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public Key: (1024 bit)
Modulus (1024 bit):
00:ce:5d:22:d0:1b:a0:6d:55:48:6c:7b:3e:51:0e:
4e:43:fd:20:0d:ee:f1:62:cc:bd:fd:1b:bf:d0:0b:
38:9b:a0:a7:d2:74:1e:36:77:d5:3b:82:4a:6a:09:
80:c4:13:25:4d:29:5d:b5:a8:39:8d:3e:27:36:c9:
8e:45:d1:84:b0:1d:ce:91:9b:75:dd:b6:55:a9:f6:
ec:bd:d5:70:b8:c8:63:c9:37:50:e3:2e:89:92:95:
7f:eb:5e:54:d3:6f:67:7a:12:f4:12:2d:b5:95:0d:
d5:7d:82:33:88:f8:af:40:f8:63:10:c4:37:ae:f8:
29:39:00:3a:4e:57:2c:0c:25
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Subject Key Identifier:
8E:8A:F0:5D:3A:B5:49:95:8F:E4:66:CB:64:86:97:94:67:65:A6:4F
X509v3 Authority Key Identifier:
keyid:F9:79:19:E7:91:26:27:24:EC:78:65:8C:BB:CD:10:8F:A2:1A:DC:05
X509v3 Subject Alternative Name:
DNS:exporter.example.com
Signature Algorithm: sha1WithRSAEncryption
0d:a8:6e:94:38:87:ad:80:91:b7:5e:4f:1c:8a:09:2d:09:67:
fe:ff:25:9e:a3:03:78:53:5a:da:ff:22:9c:e9:63:af:f2:e2:
8e:04:23:92:d8:df:5b:40:0d:a5:2f:df:2b:7c:30:6e:34:88:
bc:bc:b5:64:2e:3a:8a:3b:c4:77:9f:3e:a0:a8:dc:e6:00:59:
2e:48:2f:63:1d:ee:91:d1:9e:fc:70:5b:a2:79:70:64:e7:57:
36:de:90:3f:1a:0f:83:0b:2a:e5:8a:06:7f:8f:b3:46:f4:f2:
f9:1e:7f:bc:39:54:41:8f:94:1f:a8:43:ff:4e:a5:36:34:75:
7b:45
-----BEGIN CERTIFICATE-----
MIICFDCCAX2gAwIBAgIBAjANBgkqhkiG9w0BAQUFADAdMRswGQYDVQQDExJWZXJt
b250IGV4YW1wbGUgQ0EwHhcNMDkwMzAzMTczMjM3WhcNMjkwMjI2MTczMjM3WjAT
MREwDwYDVQQDEwhFeHBvcnRlcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
zl0i0BugbVVIbHs+UQ5OQ/0gDe7xYsy9/Ru/0As4m6Cn0nQeNnfVO4JKagmAxBMl
TSldtag5jT4nNsmORdGEsB3OkZt13bZVqfbsvdVwuMhjyTdQ4y6JkpV/615U029n
ehL0Ei21lQ3VfYIziPivQPhjEMQ3rvgpOQA6TlcsDCUCAwEAAaNuMGwwCQYDVR0T
BAIwADAdBgNVHQ4EFgQUjorwXTq1SZWP5GbLZIaXlGdlpk8wHwYDVR0jBBgwFoAU
+XkZ55EmJyTseGWMu80Qj6Ia3AUwHwYDVR0RBBgwFoIUZXhwb3J0ZXIuZXhhbXBs
ZS5jb20wDQYJKoZIhvcNAQEFBQADgYEADahulDiHrYCRt15PHIoJLQln/v8lnqMD
eFNa2v8inOljr/LijgQjktjfW0ANpS/fK3wwbjSIvLy1ZC46ijvEd58+oKjc5gBZ
LkgvYx3ukdGe/HBbonlwZOdXNt6QPxoPgwsq5YoGf4+zRvTy+R5/vDlUQY+UH6hD
/06lNjR1e0U=
-----END CERTIFICATE-----

View File

@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDOXSLQG6BtVUhsez5RDk5D/SAN7vFizL39G7/QCziboKfSdB42
d9U7gkpqCYDEEyVNKV21qDmNPic2yY5F0YSwHc6Rm3XdtlWp9uy91XC4yGPJN1Dj
LomSlX/rXlTTb2d6EvQSLbWVDdV9gjOI+K9A+GMQxDeu+Ck5ADpOVywMJQIDAQAB
AoGAKvC8Xrtw7W8yi8g1Vl54sLMKKXwP5HQgEHvmtN6w38lVQniBFOpoh2J8I4Zo
seQd/eleo38mwpotRLw6C5MmXbNud9IJtS3CJ0ScexhOhrJUKSFRx/aa0F3aUPW/
QV4FzHHu+Yfmd2sJm4W9JiFWa8KkMNQdJ9Nih4ITCyZapv0CQQD0tIIJbxp+zWbS
bq8ltz4luN9SDp1kgju/h8DFKsJhHZssxkBmURfg0O+ZgfixSorL5jtA7rB0AUhG
vwgeTiI7AkEA1+OUE2CkGip5fkRbOcjHTgtIZ+XoJRkZNkv9KvJ6rucP/oANiLFM
xV/j/jG5wcK9s0snPg13xrKdTqMa8bRFHwJBAJRP51SiczOReJVoIl3AnzkrwKay
VvC5Ak+Gju6xiNhlokxT6GpbEhbfa6jlnn6OCGumohkr0eStdknytI/xmUsCQGJN
3JVeQhswEBZw5eFQDYD6HkRBKg4KebKBs4wk0bxmtp+6i28c3MpbOaP73IvgMyU7
KWlWFJ5DouG134UEAx0CQQC1AYzV2XRi/g5G3kPD2M0esvItytOa9S7IysWasohc
ntl3dl62hZhOCJfudppsEn498Y5O8zjq070i/Zzsmxvk
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,14 @@
-----BEGIN CERTIFICATE-----
MIICNjCCAZ+gAwIBAgIJAPzZNYkMsvubMA0GCSqGSIb3DQEBBQUAMB0xGzAZBgNV
BAMTElZlcm1vbnQgZXhhbXBsZSBDQTAeFw0wOTAzMDMxNzIzMDRaFw0yOTAyMjYx
NzIzMDRaMB0xGzAZBgNVBAMTElZlcm1vbnQgZXhhbXBsZSBDQTCBnzANBgkqhkiG
9w0BAQEFAAOBjQAwgYkCgYEApZJqq1NMAZRMOJbIxCcHCNmb4dX4bfhvVknhJDig
vbwkBBhjEwHjobiFdEntaySUO/VxlXO9SrbyuF+39gNqtxkJvl2AQ1cy826s7aX3
deE+A9LoC8WRiEX8bwqwQCDbHF5Ue3yjZVrtScyAgURuGePdGBla9shgpKqZf9yR
W9ECAwEAAaN+MHwwHQYDVR0OBBYEFPl5GeeRJick7HhljLvNEI+iGtwFME0GA1Ud
IwRGMESAFPl5GeeRJick7HhljLvNEI+iGtwFoSGkHzAdMRswGQYDVQQDExJWZXJt
b250IGV4YW1wbGUgQ0GCCQD82TWJDLL7mzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3
DQEBBQUAA4GBADolIeFcm4sX65qS61hS9wZAXnpvCvhu0SFylBLhEYiL+D8QUzx9
Mtbtwhih60WGb5IBJ6M1QwTTKtSTbPTWK5UoQK2+xjh5IvMVCHDRopCy1jKhIzX7
/rtFZ9mXyjyLINvf1Q8k8djvXgoGsXQrZdQE4+TRTKVMpn8tFSrFHqEx
-----END CERTIFICATE-----

130
configs/sctp_exporter.xml Normal file
View File

@ -0,0 +1,130 @@
<ipfixConfig>
<sensorManager id="99">
<checkinterval>2</checkinterval>
</sensorManager>
<observer id="1">
<filename>../sample_data/sample1.cap</filename>
<pcap_filter>ip</pcap_filter>
<!-- offlineSpeed>-1</offlineSpeed -->
<next>2</next>
</observer>
<packetQueue id="2">
<maxSize>10</maxSize>
<next>6</next>
</packetQueue>
<packetAggregator id="6">
<rule>
<templateId>998</templateId>
<flowKey>
<ieName>sourceIPv4Address</ieName>
</flowKey>
<flowKey>
<ieName>destinationIPv4Address</ieName>
</flowKey>
<flowKey>
<ieName>protocolIdentifier</ieName>
</flowKey>
<flowKey>
<ieName>sourceTransportPort</ieName>
</flowKey>
<flowKey>
<ieName>destinationTransportPort</ieName>
</flowKey>
<nonFlowKey>
<ieName>flowStartMilliSeconds</ieName>
</nonFlowKey>
<nonFlowKey>
<ieName>flowEndMilliSeconds</ieName>
</nonFlowKey>
<nonFlowKey>
<ieName>octetDeltaCount</ieName>
</nonFlowKey>
<nonFlowKey>
<ieName>packetDeltaCount</ieName>
</nonFlowKey>
<nonFlowKey>
<ieName>tcpControlBits</ieName>
</nonFlowKey>
</rule>
<expiration>
<inactiveTimeout unit="sec">1</inactiveTimeout>
<activeTimeout unit="sec">1</activeTimeout>
</expiration>
<pollInterval unit="msec">1000</pollInterval>
<next>4</next>
</packetAggregator>
<ipfixAggregator id="4">
<rule>
<templateId>999</templateId>
<biflowAggregation>1</biflowAggregation>
<flowKey>
<ieName>sourceIPv4Address</ieName>
</flowKey>
<flowKey>
<ieName>destinationIPv4Address</ieName>
</flowKey>
<flowKey>
<ieName>protocolIdentifier</ieName>
</flowKey>
<flowKey>
<ieName>sourceTransportPort</ieName>
</flowKey>
<flowKey>
<ieName>destinationTransportPort</ieName>
</flowKey>
<nonFlowKey>
<ieName>flowStartMilliSeconds</ieName>
</nonFlowKey>
<nonFlowKey>
<ieName>flowEndMilliSeconds</ieName>
</nonFlowKey>
<nonFlowKey>
<ieName>octetDeltaCount</ieName>
</nonFlowKey>
<nonFlowKey>
<ieName>packetDeltaCount</ieName>
</nonFlowKey>
<nonFlowKey>
<ieName>tcpControlBits</ieName>
</nonFlowKey>
<nonFlowKey>
<ieName>revflowStartMilliSeconds</ieName>
</nonFlowKey>
<nonFlowKey>
<ieName>revflowEndMilliSeconds</ieName>
</nonFlowKey>
<nonFlowKey>
<ieName>revoctetDeltaCount</ieName>
</nonFlowKey>
<nonFlowKey>
<ieName>revpacketDeltaCount</ieName>
</nonFlowKey>
<nonFlowKey>
<ieName>revtcpControlBits</ieName>
</nonFlowKey>
</rule>
<expiration>
<inactiveTimeout unit="sec">1</inactiveTimeout>
<activeTimeout unit="sec">1</activeTimeout>
</expiration>
<pollInterval unit="msec">1000</pollInterval>
<next>7</next>
</ipfixAggregator>
<ipfixQueue id="7">
<entries>1</entries>
<next>8</next>
</ipfixQueue>
<ipfixExporter id="8">
<collector>
<ipAddressType>4</ipAddressType>
<ipAddress>127.0.0.1</ipAddress>
<transportProtocol>SCTP</transportProtocol>
<port>4433</port>
</collector>
</ipfixExporter>
</ipfixConfig>

13
configs/sctp_printer.xml Normal file
View File

@ -0,0 +1,13 @@
<ipfixConfig>
<ipfixCollector id="1">
<listener>
<ipAddressType>4</ipAddressType>
<transportProtocol>SCTP</transportProtocol>
<port>4433</port>
</listener>
<next>2</next>
</ipfixCollector>
<ipfixPrinter id="2">
</ipfixPrinter>
</ipfixConfig>

71
configs/udp_exporter.xml Normal file
View File

@ -0,0 +1,71 @@
<ipfixConfig>
<sensorManager id="99">
<checkinterval>2</checkinterval>
</sensorManager>
<observer id="1">
<filename>../sample_data/sample1.cap</filename>
<pcap_filter>ip</pcap_filter>
<!-- offlineSpeed>-1</offlineSpeed -->
<next>2</next>
</observer>
<packetQueue id="2">
<maxSize>10</maxSize>
<next>6</next>
</packetQueue>
<packetAggregator id="6">
<rule>
<templateId>998</templateId>
<flowKey>
<ieName>sourceIPv4Address</ieName>
</flowKey>
<flowKey>
<ieName>destinationIPv4Address</ieName>
</flowKey>
<flowKey>
<ieName>protocolIdentifier</ieName>
</flowKey>
<flowKey>
<ieName>sourceTransportPort</ieName>
</flowKey>
<flowKey>
<ieName>destinationTransportPort</ieName>
</flowKey>
<nonFlowKey>
<ieName>flowStartMilliSeconds</ieName>
</nonFlowKey>
<nonFlowKey>
<ieName>flowEndMilliSeconds</ieName>
</nonFlowKey>
<nonFlowKey>
<ieName>octetDeltaCount</ieName>
</nonFlowKey>
<nonFlowKey>
<ieName>packetDeltaCount</ieName>
</nonFlowKey>
<nonFlowKey>
<ieName>tcpControlBits</ieName>
</nonFlowKey>
</rule>
<expiration>
<inactiveTimeout unit="sec">1</inactiveTimeout>
<activeTimeout unit="sec">1</activeTimeout>
</expiration>
<pollInterval unit="msec">1000</pollInterval>
<next>7</next>
</packetAggregator>
<ipfixQueue id="7">
<entries>1</entries>
<next>8</next>
</ipfixQueue>
<ipfixExporter id="8">
<collector>
<ipAddressType>4</ipAddressType>
<ipAddress>127.0.0.1</ipAddress>
<transportProtocol>UDP</transportProtocol>
</collector>
</ipfixExporter>
</ipfixConfig>

11
configs/udp_printer.xml Normal file
View File

@ -0,0 +1,11 @@
<ipfixConfig>
<ipfixCollector id="1">
<listener>
<transportProtocol>UDP</transportProtocol>
</listener>
<next>2</next>
</ipfixCollector>
<ipfixPrinter id="2">
</ipfixPrinter>
</ipfixConfig>

View File

@ -30,6 +30,8 @@ ADD_LIBRARY(common
cryptopan/rijndael.cpp
hmacsha1/sha1.cpp
hmacsha1/sha1_hmac.cpp
openssl/OpenSSL.cpp
openssl/SSLCTXWrapper.cpp
)
SUBDIRS(

View File

@ -67,6 +67,14 @@
*/
#define IS_DEFAULT_SCTP_DATALIFETIME 10000
/**
* defines amount of seconds, before a new DTLS connection setup is
* initiated to replace the existing DTLS connection. This mechanism
* aims to overcome the dead peer problem.
*
*/
#define IS_DEFAULT_DTLS_CONNECTIONLIFETIME 3600
/**
* defines interval in seconds, how often IpfixExporter tries to reestablish a
* lost SCTP connection

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,7 @@
jan@petranek.de
*/
#include "encoding.h"
#include "common/msg.h"
#ifdef __cplusplus
extern "C" {
@ -74,7 +75,7 @@ uint64_t ntohll(uint64_t number)
int write_octet ( char** p_pos, char* p_end, uint8_t n)
{
if (p_end < ( *p_pos + sizeof(uint8_t) ) ) {
fprintf(stderr, "error in write_octet: buffer too small!\n");
msg(MSG_ERROR, "error in write_octet: buffer too small!");
return -1;
}
uint8_t uint8 = n;
@ -97,7 +98,7 @@ int write_unsigned16 ( char** p_pos, char* p_end, uint16_t n)
{
if (p_end < ( *p_pos + sizeof(uint16_t) ) ) {
fprintf(stderr, "error in write_unsigned16: buffer too small!\n");
msg(MSG_ERROR, "error in write_unsigned16: buffer too small!");
return -1;
}
uint16_t uint16=htons(n);
@ -119,7 +120,7 @@ int write_unsigned16 ( char** p_pos, char* p_end, uint16_t n)
int write_unsigned32 ( char** p_pos, char* p_end, uint32_t n)
{
if (p_end < ( *p_pos + sizeof(uint32_t) ) ) {
fprintf(stderr, "error in write_unsigned32: buffer too small!\n");
msg(MSG_ERROR, "error in write_unsigned32: buffer too small!");
return -1;
}
uint32_t uint32 = htonl (n);
@ -155,7 +156,7 @@ int write_ipv4Address ( char** p_pos, char* p_end, uint32_t n)
int write_unsigned64 ( char** p_pos, char* p_end, uint64_t n)
{
if (p_end < ( *p_pos + sizeof(uint64_t) ) ) {
fprintf(stderr, "error in write_unsigned64: buffer too small!\n");
msg(MSG_ERROR, "error in write_unsigned64: buffer too small!");
return -1;
}
uint64_t uint64 = htonll (n);
@ -177,7 +178,7 @@ int write_unsigned64 ( char** p_pos, char* p_end, uint64_t n)
int write_float32 ( char** p_base, char* p_end, float f)
{
if (p_end < ( *p_base + sizeof(float) ) ) {
fprintf(stderr, "error in write_float32: buffer too small!\n");
msg(MSG_ERROR, "error in write_float32: buffer too small!");
return -1;
}
@ -230,7 +231,7 @@ int write_boolean(char **p_pos, char *p_end, char b)
uint8_t read_octet(char **p_pos, char *p_end)
{
if (p_end < ( *p_pos + sizeof(uint8_t) ) ) {
fprintf(stderr, "error in read_octet: buffer too small!\n");
msg(MSG_ERROR, "error in read_octet: buffer too small!");
return -1;
}
@ -251,7 +252,7 @@ uint8_t read_octet(char **p_pos, char *p_end)
uint16_t read_unsigned16(char **p_pos, char *p_end)
{
if (p_end < ( *p_pos + sizeof(uint16_t) ) ) {
fprintf(stderr, "error in read_unsigned16: buffer too small!\n");
msg(MSG_ERROR, "error in read_unsigned16: buffer too small!");
return -1;
}
// **p_pos is a pointer pointer.
@ -277,7 +278,7 @@ uint16_t read_unsigned16(char **p_pos, char *p_end)
uint32_t read_unsigned32(char **p_pos, char *p_end)
{
if (p_end < ( *p_pos + sizeof(uint32_t) ) ) {
fprintf(stderr, "error in read_unsigned32: buffer too small!\n");
msg(MSG_ERROR, "error in read_unsigned32: buffer too small!");
return -1;
}
// **p_pos is a pointer pointer.
@ -318,7 +319,7 @@ float read_float32(char **p_base, char *p_end)
{
// we assume, all we need is to convert the float to network-byte-order
if (p_end < ( *p_base + sizeof(float) ) ) {
fprintf(stderr, "error in read_float32: buffer too small!\n");
msg(MSG_ERROR, "error in read_float32: buffer too small!");
return -1;
}
float f = 2.1; // initialize the float to some dummy value.
@ -349,7 +350,7 @@ float read_float32(char **p_base, char *p_end)
uint64_t read_unsigned64 ( char** p_pos, char* p_end)
{
if (p_end < ( *p_pos + sizeof(uint64_t) ) ) {
fprintf(stderr, "error in read_unsigned64: buffer too small!\n");
msg(MSG_ERROR, "error in read_unsigned64: buffer too small!");
return -1;
}
// **p_pos is a pointer pointer.
@ -387,7 +388,7 @@ int read_octet_array(char **p_pos, char *p_end, char *p_output)
first_len=read_octet(p_pos, p_end);
// simply must work!
/* if (first_len == -1) { */
/* fprintf(stderr, "error in read_octet_array: reading first length byte failed!\n"); */
/* msg(MSG_ERROR, "error in read_octet_array: reading first length byte failed!"); */
/* return -1; */
/* } */
@ -397,13 +398,9 @@ int read_octet_array(char **p_pos, char *p_end, char *p_output)
// read another 2 bytes
data_length = read_unsigned16 (p_pos, p_end);
if (data_length < 255 ) {
fprintf(stderr, "scream!!!\n");
msg(MSG_ERROR, "malformed array of octets");
}
}
printf (" read_octet_array: data_length %u \n", data_length);
printf ("%s\n", *p_pos);
printf ("return\n");
memcpy (p_output, *p_pos, data_length);
p_pos += data_length;
@ -428,7 +425,7 @@ int write_extension_and_fieldID(char** p_pos, char* p_end, uint16_t fieldID)
//uint16_t leading_bit = 1 << 15;
if (p_end < ( *p_pos + sizeof(uint16_t) ) ) {
fprintf(stderr, "error in write_unsigned16: buffer too small!\n");
msg(MSG_ERROR, "error in write_unsigned16: buffer too small!");
return -1;
}
@ -459,7 +456,7 @@ int read_extension_bit(char **p_pos, char *p_end)
uint16_t n = *( (uint16_t*) *p_pos);
if (p_end < ( *p_pos + sizeof(uint16_t) ) ) {
fprintf(stderr, "error in write_unsigned16: buffer too small!\n");
msg(MSG_ERROR, "error in write_unsigned16: buffer too small!");
return -1;
}
@ -482,7 +479,7 @@ int read_extension_bit(char **p_pos, char *p_end)
uint16_t read_fieldID(char **p_pos, char *p_end)
{
if (p_end < ( *p_pos + sizeof(uint16_t) ) ) {
fprintf(stderr, "error in read_unsigned16: buffer too small!\n");
msg(MSG_ERROR, "error in read_unsigned16: buffer too small!");
return -1;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,4 @@
/* vim: set sts=4 sw=4 cindent nowrap: This modeline was added by Daniel Mentz */
#ifndef IPFIXLOLIB_H
#define IPFIXLOLIB_H
@ -23,6 +24,79 @@
jan@petranek.de
*/
/*! \mainpage ipfixlolib documentation
ipfixlolib is a library that implements an IPFIX Exporter as defined in RFC 5101. It supports the following transport protocols
- UDP
- SCTP
- DTLS over UDP
- DTLS over SCTP
See ipfixlolib.h for details on how to use this library.
*/
/*! \file ipfixlolib.h
\brief Interface to ipfixlolib
ipfixlolib implements an IPFIX Exporter. It supports the following
protocols
- UDP
- SCTP
- DTLS over UDP
- DTLS over SCTP
ipfixlolib should be used in the following way:
- Applications start out by creating an exporter object using
ipfix_init_exporter(). The exporter object is the abstraction for a common
set of active Templates, a common send buffer, an observation domain id, a
sequence number and further information. Most of the functions described
below require a previously initialized exporter as the first argument.
- Multiple Collectors can be added to the exporter by using
ipfix_add_collector(). The transport protocol, the Collector's IP address
and port as well as other configuration details will be passed to
ipfix_add_collector(). ipfixlolib will start the connection setup procedure
immediately which includes triggering the DTLS handshake if one of the DTLS
variants has been chosen as the transport protocol.
- ipfix_start_template(), ipfix_put_template_field() and
ipfix_end_template() can be used at any time throughout the life cycle
of an exporter to define new Templates. These Template definitions will not
be sent immediately, but when ipfix_send() is called the next time. If UDP
is used as the transport protocol, the library makes sure that active
Templates are retransmitted on a regular basis as required by RFC 5101. It
should be noted that the library supports only one Template per Template
Set even though RFC 5101 allows multiple Templates per Set. Templates can
only be defined on a per Exporter basis i.e. <em>all</em> Templates will be
send to <em>all</em> Collectors. You cannot define a Template that is valid
only for a specific Collector.
- ipfix_remove_template_set() removes previously defined Templates that are
no longer needed. A corresponding withdrawal message is sent to all
Collectors using SCTP.
- The functions ipfix_start_data_set(), ipfix_put_data_field(),
ipfix_end_data_set() are used to append Data Sets to the send buffer. Again,
there is only a single send buffer per exporter. As soon as ipfix_send() is
called, all Data Sets will be sent to all Collectors in parallel.
- Depending on the transport protocols used and the network infrastructure
(e.g., the Maximum Transmission Unit (MTU)), IPFIX Messages might be
limited to a certain length. As a result, the application has to ensure
that it does not overshoot this length by appending too much data to the
send buffer. ipfix_get_remaining_space() should be used to query how many
bytes still may be added. It should be noted that there is a 4 bytes
overhead per Data Set i.e. calling ipfix_start_data_set() adds 4 extra
bytes to the send buffer. The macro IPFIX_OVERHEAD_PER_SET which is equal to
4 should be used instead of hard wiring the value 4.
- ipfix_send() transmits all Templates that have been previously defined
but not yet transmitted. This function also retransmits Templates to UDP
Collectors on a regular basis as required by RFC 5101. In addition, all
Data Sets waiting in the send buffer are transmitted. The length of this
buffer is reset to zero afterwards.
- ipfix_remove_collector() can be used at any time to remove a Collector
that has been previously added with ipfix_add_collector(). This includes
closing the transport connection.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
@ -37,12 +111,15 @@
#include <unistd.h>
#include <errno.h>
#include <string.h>
#ifdef SUPPORT_SCTP
#ifdef SUPPORT_SCTP
#include <netinet/sctp.h>
#endif
//#include "encoding.h"
//#include "ipfix_names.h"
#ifdef SUPPORT_DTLS
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/bio.h>
#include "common/openssl/OpenSSL.h"
#endif
#ifdef __cplusplus
extern "C" {
@ -59,8 +136,8 @@ extern "C" {
#define IPFIX_ENTERPRISE_BIT (1 << 15)
/*
* amount of iovec, the header consumes
* change only, if you change the internal code!
* Amount of iovec, the header consumes.
* Change only, if you change the internal code!
*/
#define HEADER_USED_IOVEC_COUNT 1
@ -71,7 +148,7 @@ extern "C" {
#define IPFIX_MAX_COLLECTORS 16
/*
* maximum number of templates at a time
* maximum number of templates at a time;
* can be specified by user
*/
#define IPFIX_MAX_TEMPLATES 16
@ -82,8 +159,8 @@ extern "C" {
* can be specified by user
*/
#define IPFIX_DEFAULT_TEMPLATE_TIMER 20
/*
* Default time, until a new SCTP retransmission attempt
/*
* Default time, until a new SCTP retransmission attempt
* takes place
* 5 minutes = 400 seconds
* can be specified by user
@ -96,138 +173,206 @@ extern "C" {
*/
#define IPFIX_DEFAULT_SCTP_DATA_LIFETIME 0
#define TRUE 1
#define FALSE 0
/*
* maximum number of sets per IPFIX packet
* TODO: This value is delibaretely chosen, adapt it if you need it or make it dynamic.
* TODO: This value is deliberately chosen, adapt it if you need it or make it dynamic.
*/
#define IPFIX_MAX_SETS_PER_PACKET 4
#define IPFIX_MAX_SETS_PER_PACKET 128
/*
* maximum size of a sendbuffer
* TODO: This value is delibaretely chosen, adapt it if you need it or make it dynamic.
* TODO: This value is deliberately chosen, adapt it if you need it or make it dynamic.
*/
#define IPFIX_MAX_SENDBUFSIZE 1024
#define IPFIX_MAX_SENDBUFSIZE (32 * 1024)
/*
* This macro appends data to the sendbuffer. If the sendbuffer is too small,
* it will print an error message and set errno to -1.
* maximum size of an IPFIX packet
*/
#define IPFIX_MAX_PACKETSIZE (1<<16)
/* MTU considerations apply to UDP and DTLS over UDP only. */
/* The MTU is set by the user. Path MTU discovery is turned off. */
#define IPFIX_MTU_FIXED 0
/* Path MTU discovery is turned on. */
#define IPFIX_MTU_DISCOVER 1
/* #define ipfix_add_data2sendbuffer(SENDBUF, POINTER, LENGTH) { \ */
/* if ((*SENDBUF).current >= (*SENDBUF).length-2 ) { \ */
/* fprintf (stderr, "Error: Sendbuffer too small to handle %i entries!\n", (*SENDBUF).current ); \ */
/* errno = -1; \ */
/* } \ */
/* ((*SENDBUF).entries[ (*SENDBUF).current ]).iov_base = POINTER; \ */
/* ((*SENDBUF).entries[ (*SENDBUF).current ]).iov_len = LENGTH; \ */
/* (*SENDBUF).current++; \ */
/* } */
/*
#define ipfix_put_field2sendbuffer(SENDBUF, POINTER, LENGTH) { \
if ((SENDBUF->current + 1) >= IPFIX_MAX_SENDBUFSIZE ) { \
msg(MSG_ERROR, "IPFIX: Sendbuffer too small to handle %i entries!\n", SENDBUF->current ); \
errno = -1; \
} \
(SENDBUF->entries[ SENDBUF->current ]).iov_base = POINTER; \
(SENDBUF->entries[ SENDBUF->current ]).iov_len = LENGTH; \
SENDBUF->current++; \
(SENDBUF->set_manager).data_length+= LENGTH; \
}
*/
* Stevens: The maximum size of an IPv4 datagram is 65535 bytes, including
* the IPv4 header. This is because of the 16-bit total length field.
*/
#define IPFIX_MTU_MAX UINT16_MAX
/* Use a very conservative default MTU so that it even works with IPSec over PPPoE */
#define IPFIX_MTU_CONSERVATIVE_DEFAULT 1400
/* BUGFIX: After the makro found an error condition, it skips accessing data. */
#define ipfix_put_data_field(EXPORTER, POINTER, LENGTH) { \
if (EXPORTER->data_sendbuffer->current >= IPFIX_MAX_SENDBUFSIZE) { \
msg(MSG_ERROR, "IPFIX: Sendbuffer too small to handle %i entries!\n", EXPORTER->data_sendbuffer->current ); \
errno = -1; \
} else { \
(EXPORTER->data_sendbuffer->entries[ EXPORTER->data_sendbuffer->current ]).iov_base = POINTER; \
(EXPORTER->data_sendbuffer->entries[ EXPORTER->data_sendbuffer->current ]).iov_len = LENGTH; \
EXPORTER->data_sendbuffer->current++; \
(EXPORTER->data_sendbuffer->set_manager).data_length+= LENGTH; \
} \
}
#ifdef IP_MTU_DISCOVER
/* basically Linux */
#define IPFIX_MTU_DEFAULT IPFIX_MTU_MAX
#define IPFIX_MTU_MODE_DEFAULT IPFIX_MTU_DISCOVER
#else
/* non-Linux, mostly FreeBSD */
#define IPFIX_MTU_DEFAULT IPFIX_MTU_CONSERVATIVE_DEFAULT
#define IPFIX_MTU_MODE_DEFAULT IPFIX_MTU_FIXED
#endif
#ifdef SUPPORT_DTLS
#define IPFIX_DTLS_MAX_RECORD_LENGTH 16384
#endif
/* Struct containing an ipfix-header */
/* Header Format (see draft-ietf-ipfix-protocol-03.txt) */
/* Header Format (see RFC 5101)
/* 0 1 2 3 */
/* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 */
3.1. Message Header Format
/* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */
/* | Version Number | Length | */
/* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */
/* | Export Time | */
/* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */
/* | Sequence Number | */
/* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */
/* | Source ID | */
/* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */
/* Message Header Field Descriptions */
/* Version */
/* Version of Flow Record format exported in this message. The */
/* value of this field is 0x000a for the current version. */
The format of the IPFIX Message Header is shown in Figure F.
/* Length */
/* Total Length is the length of the IPFIX message, measured in */
/* octets, including message Header and FlowSet(s). */
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Version Number | Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Export Time |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Observation Domain ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/* Export Time */
/* Time in seconds since 0000 UTC 1970, at which the Export */
/* Packet leaves the Exporter. */
Figure F: IPFIX Message Header Format
/* Sequence Number */
/* Incremental sequence counter of all IPFIX Messages sent from */
/* the current Observation Domain by the Exporting Process. */
/* This value MUST SHOULD be used by the Collecting Process to */
/* identify whether any IPFIX Messages have been missed. */
Message Header Field Descriptions:
/* Source ID */
/* A 32-bit value that identifies the Exporter Process */
/* Observation Domain. Collecting Process SHOULD use the */
/* combination of the source IP address and the Source ID field */
/* to separate different export streams originating from the */
/* same Exporting Process. */
Version
Version of Flow Record format exported in this message. The value
of this field is 0x000a for the current version, incrementing by
one the version used in the NetFlow services export version 9
[RFC3954].
Length
Total length of the IPFIX Message, measured in octets, including
Message Header and Set(s).
Export Time
Time, in seconds, since 0000 UTC Jan 1, 1970, at which the IPFIX
Message Header leaves the Exporter.
Sequence Number
Incremental sequence counter modulo 2^32 of all IPFIX Data Records
sent on this PR-SCTP stream from the current Observation Domain by
the Exporting Process. Check the specific meaning of this field
in the subsections of Section 10 when UDP or TCP is selected as
the transport protocol. This value SHOULD be used by the
Collecting Process to identify whether any IPFIX Data Records have
been missed. Template and Options Template Records do not
increase the Sequence Number.
Observation Domain ID
A 32-bit identifier of the Observation Domain that is locally
unique to the Exporting Process. The Exporting Process uses the
Observation Domain ID to uniquely identify to the Collecting
Process the Observation Domain that metered the Flows. It is
RECOMMENDED that this identifier also be unique per IPFIX Device.
Collecting Processes SHOULD use the Transport Session and the
Observation Domain ID field to separate different export streams
originating from the same Exporting Process. The Observation
Domain ID SHOULD be 0 when no specific Observation Domain ID is
relevant for the entire IPFIX Message, for example, when exporting
the Exporting Process Statistics, or in case of a hierarchy of
Collectors when aggregated Data Records are exported.
*/
typedef struct {
uint16_t version;
uint16_t length;
uint32_t export_time;
uint32_t sequence_number;
uint32_t source_id;
uint32_t observation_domain_id;
} ipfix_header;
/* Set Header:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Set ID | Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Set ID | Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
/* Note that this ipfix_set_header struct is only used for data sets.
* The header of template sets is built up in a char array.
* (See ipfix_start_datatemplate)
*/
typedef struct {
uint16_t set_id;
uint16_t length;
} ipfix_set_header;
/*! \brief Overhead in bytes of one IPFIX Data Set. */
#define IPFIX_OVERHEAD_PER_SET 4
/*! \brief The transport protocol used to transmit IPFIX data
*/
enum ipfix_transport_protocol {
#ifdef IPFIXLOLIB_RAWDIR_SUPPORT
RAWDIR,
#endif
DATAFILE, SCTP, UDP, TCP
};
#ifdef IPFIXLOLIB_RAWDIR_SUPPORT
RAWDIR,
#endif
DATAFILE,
SCTP, /*!< SCTP, most favorable */
UDP, /*!< UDP, available on all platforms, may result in MTU issues */
TCP, /*!< TCP, currently unsupported by ipfixlolib */
DTLS_OVER_UDP, /*!< DTLS over UDP, requires OpenSSL */
DTLS_OVER_SCTP /*!< DTLS over SCTP, requires OpenSSL w/ SCTP patches from sctp.fh-muenster.de and recent version of FreeBSD */
};
typedef struct {
uint16_t mtu; /*!< Maximum transmission unit (MTU).
If set to 0, PMTU discovery will be used.
(Only available on the Linux platform)
Applies to UDP and DTLS over UDP only. */
} ipfix_aux_config_udp;
typedef struct {
const char *peer_fqdn; /*!< The Fully Qualified Domain Name (FQDN) of the
peer. If set, the peer i.e. the Collector
<em>must</em> present a certificate of which
either the subject's Common Name (CN) or one of
the subject alternative names matches the FQDN.
There is no support for wildcard matching. For the
certificate verification to work, the user must
also call <tt>ipfix_set_ca_locations()</tt> in
advance to specify the locations of the root CA
certificates.
If set to NULL, anonymous cipher suites will be
added to the list of permissible cipher suites.
The identity of the peer will not be verified
then.*/
} ipfix_aux_config_dtls;
typedef struct {
ipfix_aux_config_dtls dtls; /*!< DTLS specific configuration */
ipfix_aux_config_udp udp; /*!< UDP specific configuration */
unsigned max_connection_lifetime; /*!< Time in seconds after which the DTLS
connection is replaced by a new one.
This mechanism aims to overcome the
dead peer problem.*/
} ipfix_aux_config_dtls_over_udp;
typedef struct {
ipfix_aux_config_dtls dtls; /*!< DTLS specific configuration */
} ipfix_aux_config_dtls_over_sctp;
/*
* These indicate, if a field is commited (i.e. can be used)
* These indicate, if a field is committed (i.e. can be used)
* unused or unclean (i.e. data is not complete yet)
* T_SENT (Template was sent) and T_WiTHDRAWN (Template destroyed)
* T_SENT (Template was sent) and T_WITHDRAWN (Template destroyed)
* are used with SCTP, since Templates are sent only once
* T_TOBEDELETED templates will be deleted the next time when the buffer is updated
*/
@ -240,6 +385,25 @@ enum template_state {T_UNUSED, T_UNCLEAN, T_COMMITED, T_SENT, T_WITHDRAWN, T_TOB
* sets the state to C_CONNECTED. If connection is lost and the socket closed
* state changes to C_DISCONNECTED and reconnection attempts can take place
*/
/* The life cycles of connections of type DTLS over UDP
* and plain UDP are as follows:
*
* DTLS over UDP:
* - state == C_UNUSED
* - Successful calls to socket() and connect()
* - state <= C_NEW
* - DTLS handshake is taking place
* - DTLS handshake succeeded.
* - Templates are sent
* - state <= C_CONNECTED
*
* UDP:
* - state == C_UNUSED
* - Successful calls to socket() and connect()
* - state <= C_NEW
* - Templates are sent
* - state <= C_CONNECTED
*/
enum collector_state {C_UNUSED, C_NEW, C_DISCONNECTED, C_CONNECTED};
@ -247,43 +411,79 @@ enum collector_state {C_UNUSED, C_NEW, C_DISCONNECTED, C_CONNECTED};
* Manages a record set
* stores the record set header
*
* Please note that this struct is only used for data sets.
*
*/
typedef struct{
/* number of the current set */
/* index of the current set.
* If no set is open, then .set_counter specifies the next free entry
* in .set_header_store */
/* The maximum is IPFIX_MAX_SETS_PER_PACKET.
* set_counter serves as an index into set_header_store. */
unsigned set_counter;
/* variable that stores the position of the current set header
in ipfix_sendbuffer->entries */
struct iovec *header_iovec;
/* buffer to store set headers */
ipfix_set_header set_header_store[IPFIX_MAX_SETS_PER_PACKET];
/* set length = sum of field length */
/* This refers to the current data set only */
unsigned data_length;
} ipfix_set_manager;
/*
* A struct buffering data of an ipfix packet
* A struct buffering data of an IPFIX Message
*/
typedef struct {
struct iovec entries[IPFIX_MAX_SENDBUFSIZE]; /* an array of iovec structs, containing data and length */
/* usage of entries:
- the first HEADER_USED_IOVEC_COUNT=1 entries are reserved for the ipfix header
- the first HEADER_USED_IOVEC_COUNT=1 entries are reserved for the IPFIX header
- the remaining entries are used for
* set headers
* individual fields of the sets/records
* Example:
* entries[0]: IPFIX Message header
* entries[1]: Set header
* entries[2]: Field 1 of Data Record 1
* entries[3]: Field 2 of Data Record 1
* entries[4]: Field 1 of Data Record 2
* entries[5]: Field 2 of Data Record 2
* entries[6]: Field 1 of Data Record 3
* entries[7]: Field 2 of Data Record 3
*/
unsigned current; /* last accessed entry in entries */
unsigned committed; /* last commited entry in entries, i.e. when end_data_set was called for the last time */
unsigned current; /* next free entry in .entries */
unsigned committed; /* number of committed entries in .entries
* If ipfix_end_data_set is called, all entries
* that belong to that specific data set will be
* committed. This includes the set header as well
* as all data fields.
* If .current == .committed, then there's no
* open data set. Otherwise, there is an open
* data set. */
unsigned marker; /* marker that allows to delete recently added entries */
unsigned committed_data_length; /* length of the contained data (in bytes) */
ipfix_header packet_header;
// int uncommited_data_length; /* length of data not yet commited */
ipfix_set_manager set_manager;
unsigned committed_data_length; /* length of the contained data (in bytes)
* not including the IPFIX message header.
* This value only includes data sets for which
* ipfix_end_data_set has been called i.e.
* it does not include data sets that are still
* "open". */
ipfix_header packet_header; /* A misnomer in my (Daniel Mentz's)
opinion. Should be message_header
since it's the header of an
IPFIX Message. */
ipfix_set_manager set_manager; /* Only relevant when sendbuffer used
for data. Not relevant if used for
template sets. */
} ipfix_sendbuffer;
#ifdef SUPPORT_DTLS
typedef struct {
int socket;
// uint16_t mtu;
SSL *ssl;
time_t last_reconnect_attempt_time;
} ipfix_dtls_connection;
#endif
/*
* A collector receiving messages from this exporter
@ -293,17 +493,35 @@ typedef struct {
uint32_t port_number;
enum ipfix_transport_protocol protocol;
int data_socket; // socket data and templates are sent to
/* data_socket is NOT used for DTLS connections */
struct sockaddr_in addr;
uint32_t last_reconnect_attempt_time;
uint32_t last_reconnect_attempt_time; // applies only to SCTP and DTLS at the moment
enum collector_state state;
char *basename; /**< for protocol==FILE, this variable contains the basename for the filename */
int fh; /**< for protocol==FILE, this variable contains the file handle */
int filenum; /**< for protocol==FILE, this variable contains the current filenumber: 'filename = basename + filenum'*/
uint64_t bytes_written; /**< for protocol==FILE, this variable contains the current filesize */
uint32_t maxfilesize; /**< for protocol==FILE, this variable contains the maximum filesize given in KiB*/
char *basename; /**< for protocol==DATAFILE, this variable contains the basename for the filename */
int fh; /**< for protocol==DATAFILE, this variable contains the file handle */
int filenum; /**< for protocol==DATAFILE, this variable contains the current filenumber: 'filename = basename + filenum'*/
uint64_t bytes_written; /**< for protocol==DATAFILE, this variable contains the current filesize */
uint32_t maxfilesize; /**< for protocol==DATAFILE, this variable contains the maximum filesize given in KiB*/
int mtu_mode; /* Either IPFIX_MTU_FIXED or IPFIX_MTU_DISCOVER */
uint16_t mtu; /* Maximum transmission unit.
Applies to UDP and DTLS over UDP only. */
#ifdef IPFIXLOLIB_RAWDIR_SUPPORT
char* packet_directory_path; /**< if protocol==RAWDIR: path to a directory to store packets in. Ignored otherwise. */
int packets_written; /**< if protcol==RAWDIR: number of packets written to packet_directory_path. Ignored otherwise. */
char* packet_directory_path; /*!< if protocol==RAWDIR: path to a directory to store packets in. Ignored otherwise. */
int packets_written; /*!< if protcol==RAWDIR: number of packets written to packet_directory_path. Ignored otherwise. */
#endif
#ifdef SUPPORT_DTLS
/* Time in seconds after which a DTLS connection
* will be replaced by a new one. */
unsigned dtls_max_connection_lifetime;
unsigned dtls_connect_timeout;
ipfix_dtls_connection dtls_main;
ipfix_dtls_connection dtls_replacement;
time_t connect_time; /* point in time when the connection setup
succeeded. We need this to calculate the
age of a connection. If DTLS is used,
a connection rollover is performed when
a connection reaches a certain age.*/
const char *peer_fqdn;
#endif
} ipfix_receiving_collector;
@ -313,10 +531,23 @@ typedef struct {
typedef struct{
enum template_state state; // indicates, whether this template is valid.
uint16_t template_id;
uint16_t field_count;
int fields_length;
int max_fields_length;
char *template_fields;
uint16_t field_count; // the number of fields the user announced
// when calling ipfix_start_template
uint16_t fields_added; // make sure the user does not add more
// fields than he told us to add in the
// first place.
// Make sure fields_added <= field_count
int fields_length; // This also includes the length of the Set Header
// It's basically the number of bytes written
// into template_fields so far.
int max_fields_length; // size of the malloced char array
// template_fields points to.
char *template_fields; // This includes the Set Header and the
// Template Record Header as it goes out on the wire.
// Note that the type ipfix_set_header is *not* used
// to build Set Headers for template sets.
} ipfix_lo_template;
/*
@ -324,23 +555,34 @@ typedef struct{
* The exporting process keeps track of the sequence number.
*/
typedef struct {
uint32_t sequence_number; // total number of data records
uint32_t sequence_number; // total number of data records
uint32_t sn_increment; // to be added to sequence number before sending data records
uint32_t source_id;
uint32_t observation_domain_id;
uint16_t max_message_size; /* Maximum size of an IPFIX message.
* This is the maximum size that all collectors allow.
* If a new collector is added that only allows
* smaller IPFIX messages, this value has to be
* updated.
* Only observed when sending messages
* containing data sets. IPFIX messages
* containing template sets might get
* longer than that. That's a TODO */
ipfix_sendbuffer *template_sendbuffer;
ipfix_sendbuffer *sctp_template_sendbuffer;
ipfix_sendbuffer *data_sendbuffer;
int collector_num; // number of currently listening collectors
int collector_max_num; // maximum available collector
ipfix_receiving_collector *collector_arr; // array of collectors
ipfix_receiving_collector *collector_arr; // array of (collector_max_num) collectors
// we also need some timer / counter to indicate,
// if we should send the templates too.
// This applies only to UDP and DTLS over UDP.
// It contains the return value from time(NULL) which is the time since
// the Epoch (00:00:00 UTC, January 1, 1970), measured in seconds.
uint32_t last_template_transmission_time;
// force template send next time packets are sent (to include new template ids)
uint32_t force_template_send;
// time, after templates are transmitted again
uint32_t template_transmission_timer;
// lifetime of an SCTP data packet
@ -349,46 +591,47 @@ typedef struct {
// (0 ==> no reconnection -> destroy collector)
uint32_t sctp_reconnect_timer;
int ipfix_lo_template_maxsize;
int ipfix_lo_template_current_count;
ipfix_lo_template *template_arr;
#ifdef SUPPORT_DTLS
SSL_CTX *ssl_ctx;
const char *certificate_chain_file;
const char *private_key_file;
const char *ca_file;
const char *ca_path;
#endif
} ipfix_exporter;
int ipfix_beat(ipfix_exporter *exporter);
/* generated by genproto */
int ipfix_init_exporter(uint32_t source_id, ipfix_exporter **exporter);
int ipfix_init_exporter(uint32_t observation_domain_id, ipfix_exporter **exporter);
int ipfix_deinit_exporter(ipfix_exporter *exporter);
int ipfix_add_collector(ipfix_exporter *exporter, const char *coll_ip4_addr, int coll_port, enum ipfix_transport_protocol proto);
int ipfix_remove_collector(ipfix_exporter *exporter, char *coll_ip4_addr, int coll_port);
int ipfix_start_template_set(ipfix_exporter *exporter, uint16_t template_id, uint16_t field_count);
int ipfix_add_collector(ipfix_exporter *exporter, const char *coll_ip4_addr, int coll_port, enum ipfix_transport_protocol proto, void *aux_config);
int ipfix_remove_collector(ipfix_exporter *exporter, const char *coll_ip4_addr, int coll_port);
int ipfix_start_template(ipfix_exporter *exporter, uint16_t template_id, uint16_t field_count);
int ipfix_start_optionstemplate_set(ipfix_exporter *exporter, uint16_t template_id, uint16_t scope_length, uint16_t option_length);
int ipfix_start_datatemplate_set(ipfix_exporter *exporter, uint16_t template_id, uint16_t preceding, uint16_t field_count, uint16_t fixedfield_count);
int ipfix_start_datatemplate(ipfix_exporter *exporter, uint16_t template_id, uint16_t preceding, uint16_t field_count, uint16_t fixedfield_count);
int ipfix_put_template_field(ipfix_exporter *exporter, uint16_t template_id, uint16_t type, uint16_t length, uint32_t enterprise_id);
int ipfix_put_template_fixedfield(ipfix_exporter *exporter, uint16_t template_id, uint16_t type, uint16_t length, uint32_t enterprise_id);
int ipfix_end_template_set(ipfix_exporter *exporter, uint16_t template_id );
/* gerhard: use ipfix_remove_template_set
int ipfix_remove_template(ipfix_exporter *exporter, uint16_t template_id);
*/
int ipfix_end_template(ipfix_exporter *exporter, uint16_t template_id );
int ipfix_start_data_set(ipfix_exporter *exporter, uint16_t template_id);
uint16_t ipfix_get_remaining_space(ipfix_exporter *exporter);
int ipfix_put_data_field(ipfix_exporter *exporter,void *data, unsigned length);
int ipfix_end_data_set(ipfix_exporter *exporter, uint16_t number_of_records);
int ipfix_cancel_data_set(ipfix_exporter *exporter);
int ipfix_set_data_field_marker(ipfix_exporter *exporter);
int ipfix_delete_data_fields_upto_marker(ipfix_exporter *exporter);
int ipfix_put_template_data(ipfix_exporter *exporter, uint16_t template_id, void* data, uint16_t data_length);
int ipfix_deinit_template_set(ipfix_exporter *exporter, ipfix_lo_template* templ);
int ipfix_remove_template_set(ipfix_exporter *exporter, uint16_t template_id);
int ipfix_remove_template(ipfix_exporter *exporter, uint16_t template_id);
int ipfix_send(ipfix_exporter *exporter);
int ipfix_enterprise_flag_set(uint16_t id);
// Set up time after that Templates are going to be resent
int ipfix_set_template_transmission_timer(ipfix_exporter *exporter, uint32_t timer);
// Sets a packet lifetime for SCTP data packets (lifetime > 0 : unreliable packets)
int ipfix_set_template_transmission_timer(ipfix_exporter *exporter, uint32_t timer);
int ipfix_set_sctp_lifetime(ipfix_exporter *exporter, uint32_t lifetime);
// Set up SCTP reconnect timer, time after that a reconnection attempt is made,
// if connection to the collector was lost.
int ipfix_set_sctp_reconnect_timer(ipfix_exporter *exporter, uint32_t timer);
int ipfix_set_dtls_certificate(ipfix_exporter *exporter, const char *certificate_chain_file, const char *private_key_file);
int ipfix_set_ca_locations(ipfix_exporter *exporter, const char *ca_file, const char *ca_path);
#ifdef __cplusplus
}
#endif

View File

@ -95,7 +95,7 @@ extern "C" {
// we must lock via mutex, else logging outputs are mixed when several
// threads log simultaneously
int retval = pthread_mutex_lock(&msg_mutex);
if (retval != 0) DPRINTF("msg: pthread_mutex_lock returned error code %d", retval);
if (retval != 0) printf("!!! msg: pthread_mutex_lock returned error code %d\n", retval);
struct timeval tv;
gettimeofday(&tv, 0);
struct tm* tform = localtime(reinterpret_cast<time_t*>(&tv.tv_sec));
@ -133,7 +133,7 @@ extern "C" {
vsnprintf(logtext, EXCEPTION_MAXLEN-strlen(logtext), fmt, *args);
}
retval = pthread_mutex_unlock(&msg_mutex);
if (retval != 0) DPRINTF("msg: pthread_mutex_unlock returned error code %d", retval);
if (retval != 0) printf("!!! msg: pthread_mutex_unlock returned error code %d\n", retval);
}
}

View File

@ -42,8 +42,8 @@ typedef void (*LOGFUNCTION)(void *);
//#define MSG_DEFAULT MSG_ERROR
void msg_init();
void msg_shutdown();
void msg_init(void);
void msg_shutdown(void);
void msg2(const int, const char*, const char*, const char*, const int, const char *, ...);
void msg_setlevel(int);
int msg_stat(const char *fmt, ...);

View File

@ -0,0 +1,255 @@
/* vim: set sts=4 sw=4 cindent nowrap: This modeline was added by Daniel Mentz */
#ifdef SUPPORT_DTLS
#include "common/openssl/OpenSSL.h"
#include "common/Mutex.h"
#include "common/msg.h"
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
#include <openssl/err.h>
#include <sstream>
namespace { /* unnamed namespace */
Mutex m;
bool initialized = false; /* Determines wether OpenSSL is initialized already */
int check_x509_cert(X509 *peer, int (*cb)(void *context, const char *dnsname), void *context);
int ex_data_idx_vpcd = 0; /* Must be set by to return value from SSL_get_ex_new_index() before use */
#define SSL_ERR(c) {c,#c}
struct sslerror {
int code;
const char *str;
} sslerrors[] = {
SSL_ERR(SSL_ERROR_NONE),
SSL_ERR(SSL_ERROR_ZERO_RETURN),
SSL_ERR(SSL_ERROR_WANT_READ),
SSL_ERR(SSL_ERROR_WANT_WRITE),
SSL_ERR(SSL_ERROR_WANT_ACCEPT),
SSL_ERR(SSL_ERROR_WANT_CONNECT),
SSL_ERR(SSL_ERROR_WANT_X509_LOOKUP),
SSL_ERR(SSL_ERROR_SYSCALL),
SSL_ERR(SSL_ERROR_SSL),
};
}
void ensure_openssl_init(void) {
m.lock();
if ( ! initialized) { /* skip this if we already initialized OpenSSL */
initialized = true;
SSL_load_error_strings(); /* readable error messages */
SSL_library_init(); /* initialize library */
ex_data_idx_vpcd = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
#if 0
if (SSL_COMP_add_compression_method(0, COMP_zlib())) {
msg(MSG_ERROR, "OpenSSL: SSL_COMP_add_compression_method() failed.");
msg_openssl_errors();
};
#endif
DPRINTF("Initialized OpenSSL");
}
m.unlock();
}
/* Get errors from OpenSSL error queue and output them using msg() */
void msg_openssl_errors(void) {
char errbuf[512];
char buf[4096];
unsigned long e;
const char *file, *data;
int line, flags;
while ((e = ERR_get_error_line_data(&file,&line,&data,&flags))) {
ERR_error_string_n(e,errbuf,sizeof errbuf);
snprintf(buf, sizeof buf, "%s:%s:%d:%s", errbuf,
file, line, (flags & ERR_TXT_STRING) ? data : "");
msg(MSG_ERROR, "OpenSSL: %s",buf);
}
}
void msg_openssl_return_code(int level, const char *fn, int ret, int error) {
std::ostringstream oss;
oss << fn << " returned: " << ret;
if (ret<=0) {
oss << ", error: ";
unsigned int i;
int found=0;
for(i=0;i < sizeof(sslerrors) / sizeof(struct sslerror);i++) {
if (sslerrors[i].code==error) {
oss << sslerrors[i].str;
found=1;
break;
}
}
if (!found) {
oss << error;
}
if (error == SSL_ERROR_SYSCALL && ret==-1)
oss << ", errno: " << strerror(errno);
}
msg(level,oss.str().c_str());
msg_openssl_errors();
}
#if 0
void msg_openssl_return_code(const char *fn, int ret, int error) {
ostringstream oss;
oss << "<receivedPackets>" << statReceivedPackets << "</receivedPackets>" << endl;
return oss.str();
if (ret > 0) {
DPRINTF("%s returned: %d",fn,ret);
} else {
char buf[16];
char *s = 0;
unsigned int i;
for(i=0;i < sizeof(sslerrors) / sizeof(struct sslerror);i++) {
if (sslerrors[i].code==error) {
s = sslerrors[i].str;
break;
}
}
if (!s) {
snprintf(buf,sizeof(buf),"%d",error);
s = buf;
}
DPRINTF("%s returned: %d, error: %s",
DPRINTF("SSL_read() returned: %d, error: %d, strerror: %s",ret,error,strerror(errno));
}
#endif
const char *get_ssl_error_string(int error) {
unsigned int i;
static char unknown[] = "Unknown error code: %d";
static char s[sizeof(unknown) + 20];
for(i=0;i < sizeof(sslerrors) / sizeof(struct sslerror);i++) {
if (sslerrors[i].code==error) {
return sslerrors[i].str;
}
}
snprintf(s, sizeof(s), unknown, error); /* Not thread safe. */
return s;
}
/* returns 1 on success, 0 on error */
int verify_ssl_peer(SSL *ssl, int (*cb)(void *context, const char *dnsname), void *context) {
long verify_result;
verify_result = SSL_get_verify_result(ssl);
DPRINTF("SSL_get_verify_result() returned: %s",X509_verify_cert_error_string(verify_result));
if(verify_result!=X509_V_OK) {
msg(MSG_ERROR,"Certificate doesn't verify: %s", X509_verify_cert_error_string(verify_result));
return 0;
}
X509 *peer = SSL_get_peer_certificate(ssl);
if (! peer) {
msg(MSG_ERROR,"No peer certificate");
return 0;
}
int ret = check_x509_cert(peer, cb, context);
X509_free(peer);
return ret;
}
int verify_peer_cert_callback(int preverify_ok, X509_STORE_CTX *ctx) {
char buf[512];
SSL *ssl;
struct verify_peer_cb_data *cb_data;
X509 *cert = X509_STORE_CTX_get_current_cert(ctx);
int depth = X509_STORE_CTX_get_error_depth(ctx);
int err = X509_STORE_CTX_get_error(ctx);
if(!preverify_ok) {
msg(MSG_ERROR,"Error with certificate at depth: %i",depth);
X509_NAME_oneline(X509_get_issuer_name(cert),buf,sizeof(buf));
msg(MSG_ERROR," issuer = %s",buf);
X509_NAME_oneline(X509_get_subject_name(cert),buf,sizeof(buf));
msg(MSG_ERROR," subject = %s",buf);
msg(MSG_ERROR," err %i:%s", err, X509_verify_cert_error_string(err));
}
if (depth == 0) {
ssl = (SSL*) X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
cb_data = (verify_peer_cb_data *) SSL_get_ex_data(ssl, get_openssl_ex_data_idx_vpcd());
if (cb_data)
return check_x509_cert(cert,cb_data->cb,cb_data->context);
}
return preverify_ok;
}
int get_openssl_ex_data_idx_vpcd(void) {
return ex_data_idx_vpcd; /* Must be set by to return value from SSL_get_ex_new_index() before use */
}
namespace {
/* returns 1 on success, 0 on error */
int check_x509_cert(X509 *peer, int (*cb)(void *context, const char *dnsname), void *context) {
char buf[512];
#if DEBUG
X509_NAME_oneline(X509_get_subject_name(peer),buf,sizeof buf);
DPRINTF("peer certificate subject: %s",buf);
#endif
const STACK_OF(GENERAL_NAME) * gens;
const GENERAL_NAME *gn;
int num;
size_t len;
const char *dnsname;
gens = (STACK_OF(GENERAL_NAME) *) X509_get_ext_d2i(peer, NID_subject_alt_name, 0, 0);
num = sk_GENERAL_NAME_num(gens);
num = sk_num(((_STACK*) (1 ? (gens) : (struct stack_st_GENERAL_NAME*)0)));
for (int i = 0; i < num; ++i) {
gn = sk_GENERAL_NAME_value(gens, i);
if (gn->type != GEN_DNS)
continue;
if (ASN1_STRING_type(gn->d.ia5) != V_ASN1_IA5STRING) {
msg(MSG_ERROR, "malformed X509 cert: Type of ASN.1 string not IA5");
return 0;
}
dnsname = (char *) ASN1_STRING_data(gn->d.ia5);
len = ASN1_STRING_length(gn->d.ia5);
while(len>0 && dnsname[len-1] == 0) --len;
if (len != strlen(dnsname)) {
msg(MSG_ERROR, "malformed X509 cert");
return 0;
}
DPRINTF("Subject Alternative Name: DNS:%s",dnsname);
if ( (*cb)(context, dnsname) ) {
DPRINTF("Subject Alternative Name matched one of the "
"permitted FQDNs");
return 1;
}
}
if ((len = X509_NAME_get_text_by_NID
(X509_get_subject_name(peer),
NID_commonName, buf, sizeof buf)) <=0 ) {
DPRINTF("CN not part of certificate");
} else {
if (len != strlen(buf)) {
msg(MSG_ERROR,"malformed X509 cert: CN invalid");
return 0;
}
DPRINTF("most specific (1st) Common Name: %s",buf);
if ( (*cb)(context, buf) ) {
DPRINTF("Common Name (CN) matched one of the "
"permitted FQDNs");
return 1;
}
}
msg(MSG_ERROR,"Neither any of the Subject Alternative Names nor the Common Name "
"matched one of the permitted FQDNs");
return 0;
}
} /* unnamed namespace */
#endif /* SUPPORT_DTLS */

View File

@ -0,0 +1,33 @@
/* vim: set sts=4 sw=4 cindent nowrap: This modeline was added by Daniel Mentz */
#ifndef OPENSSLINIT_H
#define OPENSSLINIT_H
#ifdef SUPPORT_DTLS
#ifdef __cplusplus
extern "C" {
#endif
#include <openssl/ssl.h>
struct verify_peer_cb_data {
int (*cb)(void *context, const char *dnsname);
void *context;
};
void ensure_openssl_init(void);
void msg_openssl_errors(void);
void msg_openssl_return_code(int level, const char *fn, int ret, int error);
int verify_ssl_peer(SSL *ssl, int (*cb)(void *context, const char *dnsname), void *context);
int verify_peer_cert_callback(int preverify_ok, X509_STORE_CTX *ctx);
int get_openssl_ex_data_idx_vpcd(void); /* vpcd = verify_peer_cb_data */
const char *get_ssl_error_string(int ret);
#ifdef __cplusplus
}
#endif
#endif /* SUPPORT_DTLS */
#endif

View File

@ -0,0 +1,219 @@
/* vim: set sts=4 sw=4 cindent nowrap: This modeline was added by Daniel Mentz */
/*
* IPFIX Concentrator Module Library
* Copyright (C) 2004 Christoph Sommer <http://www.deltadevelopment.de/users/christoph/ipfix/>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifdef SUPPORT_DTLS
#include "SSLCTXWrapper.hpp"
#include "common/msg.h"
#include "common/openssl/OpenSSL.h"
/* Parameters for Diffie-Hellman key agreement */
DH *SSL_CTX_wrapper::get_dh2048() {
static unsigned char dh2048_p[]={
0xF6,0x42,0x57,0xB7,0x08,0x7F,0x08,0x17,0x72,0xA2,0xBA,0xD6,
0xA9,0x42,0xF3,0x05,0xE8,0xF9,0x53,0x11,0x39,0x4F,0xB6,0xF1,
0x6E,0xB9,0x4B,0x38,0x20,0xDA,0x01,0xA7,0x56,0xA3,0x14,0xE9,
0x8F,0x40,0x55,0xF3,0xD0,0x07,0xC6,0xCB,0x43,0xA9,0x94,0xAD,
0xF7,0x4C,0x64,0x86,0x49,0xF8,0x0C,0x83,0xBD,0x65,0xE9,0x17,
0xD4,0xA1,0xD3,0x50,0xF8,0xF5,0x59,0x5F,0xDC,0x76,0x52,0x4F,
0x3D,0x3D,0x8D,0xDB,0xCE,0x99,0xE1,0x57,0x92,0x59,0xCD,0xFD,
0xB8,0xAE,0x74,0x4F,0xC5,0xFC,0x76,0xBC,0x83,0xC5,0x47,0x30,
0x61,0xCE,0x7C,0xC9,0x66,0xFF,0x15,0xF9,0xBB,0xFD,0x91,0x5E,
0xC7,0x01,0xAA,0xD3,0x5B,0x9E,0x8D,0xA0,0xA5,0x72,0x3A,0xD4,
0x1A,0xF0,0xBF,0x46,0x00,0x58,0x2B,0xE5,0xF4,0x88,0xFD,0x58,
0x4E,0x49,0xDB,0xCD,0x20,0xB4,0x9D,0xE4,0x91,0x07,0x36,0x6B,
0x33,0x6C,0x38,0x0D,0x45,0x1D,0x0F,0x7C,0x88,0xB3,0x1C,0x7C,
0x5B,0x2D,0x8E,0xF6,0xF3,0xC9,0x23,0xC0,0x43,0xF0,0xA5,0x5B,
0x18,0x8D,0x8E,0xBB,0x55,0x8C,0xB8,0x5D,0x38,0xD3,0x34,0xFD,
0x7C,0x17,0x57,0x43,0xA3,0x1D,0x18,0x6C,0xDE,0x33,0x21,0x2C,
0xB5,0x2A,0xFF,0x3C,0xE1,0xB1,0x29,0x40,0x18,0x11,0x8D,0x7C,
0x84,0xA7,0x0A,0x72,0xD6,0x86,0xC4,0x03,0x19,0xC8,0x07,0x29,
0x7A,0xCA,0x95,0x0C,0xD9,0x96,0x9F,0xAB,0xD0,0x0A,0x50,0x9B,
0x02,0x46,0xD3,0x08,0x3D,0x66,0xA4,0x5D,0x41,0x9F,0x9C,0x7C,
0xBD,0x89,0x4B,0x22,0x19,0x26,0xBA,0xAB,0xA2,0x5E,0xC3,0x55,
0xE9,0x32,0x0B,0x3B,
};
static unsigned char dh2048_g[]={0x02};
DH *dh;
if ((dh=DH_new()) == NULL) return(NULL);
dh->p=BN_bin2bn(dh2048_p,sizeof(dh2048_p),NULL);
dh->g=BN_bin2bn(dh2048_g,sizeof(dh2048_g),NULL);
if ((dh->p == NULL) || (dh->g == NULL))
{ DH_free(dh); return(NULL); }
return(dh);
}
SSL_CTX_wrapper::SSL_CTX_wrapper(
const std::string &certificateChainFile,
const std::string &privateKeyFile,
const std::string &caFile,
const std::string &caPath,
bool requirePeerAuthentication
) : verify_peers(false) {
/* Several conditions have to be met before I can authenticate my peer
* (exporter):
* - [ CAfile has to be set since I have to send a Certificate
* Request to my peer and I need to know the names of the CAs to
* include in this request. (have_client_CA_list) ] This turned
* out to be wrong. I can leave the list of
* certificate_authorities empty. See RFC 4346 section 7.4.4
* - At least one of CAfile and CApath have to be set in order
* to be able to verify my peer's certificate. This is somehow
* related to the previous one. The variable have_CAs determines
* if this prerequisite is met.
* - I need to have a valid certificate including the matching
* private key. According to RFC 4346 section 7.4.4 only a
* non-anonymous server can request a certificate from the client.
* The variable have_cert determines if this prerequisite is met.
*/
bool have_CAs = false;
bool have_cert = false;
ensure_openssl_init();
ctx = SSL_CTX_new(DTLSv1_server_method());
if( ! ctx) {
THROWEXCEPTION("Failed to create SSL_CTX");
}
SSL_CTX_set_read_ahead(ctx, 1);
setDHParams();
have_CAs = loadVerifyLocations(caFile,caPath);
have_cert = loadCert(certificateChainFile,privateKeyFile);
/* We leave the certificate_authorities list of the Certificate Request
* empty. See RFC 4346 7.4.4. Certificate request.
* This implies that we are not going to call SSL_CTX_set_client_CA_list()
*/
if ( ! requirePeerAuthentication) {
DPRINTF("We are NOT going to verify the certificates of the exporters b/c "
"the peerFqdn option is NOT set.");
} else {
if ( ! (have_CAs && have_cert) ) {
msg(MSG_ERROR,"Can not verify certificates of exporters because prerequesites not met. "
"Prerequesites are: 1. CApath or CAfile or both set, "
"2. We have a certificate including the private key");
THROWEXCEPTION("Cannot verify DTLS peers.");
} else {
verify_peers = true;
SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER |
SSL_VERIFY_FAIL_IF_NO_PEER_CERT,&verify_peer_cert_callback);
DPRINTF("We are going to request certificates from the exporters "
"and we are going to verify those b/c "
"the peerFqdn option is set");
}
}
setCipherList();
}
void SSL_CTX_wrapper::setDHParams() {
// set DH parameters to be used
DH *dh = get_dh2048();
SSL_CTX_set_tmp_dh(ctx,dh);
DH_free(dh);
// generate a new DH key for each handshake
SSL_CTX_set_options(ctx,SSL_OP_SINGLE_DH_USE);
}
bool SSL_CTX_wrapper::loadVerifyLocations(
const std::string &caFile,
const std::string &caPath) {
// set default locations for trusted CA certificates
const char *CAfile = NULL;
const char *CApath = NULL;
if (!caFile.empty()) CAfile = caFile.c_str();
if (!caPath.empty()) CApath = caPath.c_str();
if (CAfile || CApath) {
if ( SSL_CTX_load_verify_locations(ctx,CAfile,CApath) ) {
return true;
} else {
msg(MSG_ERROR,"SSL_CTX_load_verify_locations() failed.");
msg_openssl_errors();
THROWEXCEPTION("Failed to open CA file / CA directory.");
}
}
return false;
}
bool SSL_CTX_wrapper::loadCert(
const std::string &certificateChainFile,
const std::string &privateKeyFile) {
// Load our own certificate
bool have_cert = false;
if (!certificateChainFile.empty()) {
const char *certificate_chain_file = certificateChainFile.c_str();
const char *private_key_file = certificateChainFile.c_str();
if (!privateKeyFile.empty()) // We expect the private key in the
// certificate file if no separate
// file for the key was specified.
private_key_file = privateKeyFile.c_str();
if (!SSL_CTX_use_certificate_chain_file(ctx, certificate_chain_file)) {
msg_openssl_errors();
THROWEXCEPTION("Unable to load certificate chain file %s",certificate_chain_file);
}
if (!SSL_CTX_use_PrivateKey_file(ctx, private_key_file, SSL_FILETYPE_PEM)) {
msg_openssl_errors();
THROWEXCEPTION("Unable to load private key file %s",private_key_file);
}
if (!SSL_CTX_check_private_key(ctx)) {
msg_openssl_errors();
THROWEXCEPTION("Private key and certificate do not match.");
}
have_cert = true;
} else if (!privateKeyFile.empty())
THROWEXCEPTION("It makes no sense specifying a private key file without "
"specifying a file that contains the corresponding certificate.");
if (have_cert)
DPRINTF("We successfully loaded our certificate.");
else
DPRINTF("We do NOT have a certificate. This means that we can only use "
"the anonymous modes of DTLS. This also implies that we can not "
"authenticate the client (exporter).");
return have_cert;
}
void SSL_CTX_wrapper::setCipherList() {
if (verify_peers) {
SSL_CTX_set_cipher_list(ctx,"DEFAULT");
} else {
SSL_CTX_set_cipher_list(ctx,"ALL"); // This includes anonymous ciphers
}
}
SSL_CTX_wrapper::~SSL_CTX_wrapper() {
DPRINTF("SSL_CTX_free(ctx)");
if (ctx) SSL_CTX_free(ctx);
}
bool SSL_CTX_wrapper::get_verify_peers() {
return verify_peers;
}
SSL *SSL_CTX_wrapper::SSL_new() {
SSL *ssl = ::SSL_new(ctx);
if( ! ssl)
THROWEXCEPTION("Cannot create SSL object");
return ssl;
}
void SSL_CTX_wrapper::SSL_free(SSL *ssl) {
::SSL_free(ssl);
}
#endif

View File

@ -0,0 +1,66 @@
/* vim: set sts=4 sw=4 cindent nowrap: This modeline was added by Daniel Mentz */
/*
* IPFIX Concentrator Module Library
* Copyright (C) 2004 Christoph Sommer <http://www.deltadevelopment.de/users/christoph/ipfix/>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef _IPFIX_SSL_CTX_WRAPPER_H_
#define _IPFIX_SSL_CTX_WRAPPER_H_
#ifdef SUPPORT_DTLS
#include <string>
#include <openssl/ssl.h>
#ifndef HEADER_DH_H
#include <openssl/dh.h>
#endif
class SSL_CTX_wrapper {
public:
SSL_CTX_wrapper(
const std::string &certificateChainFile,
const std::string &privateKeyFile,
const std::string &caFile,
const std::string &caPath,
bool requirePeerAuthentication
);
~SSL_CTX_wrapper();
SSL *SSL_new();
void SSL_free(SSL *ssl);
bool get_verify_peers();
private:
SSL_CTX *ctx;
bool verify_peers; /* Do we authenticate our peer by verifying its
certificate? */
static DH *get_dh2048();
void setDHParams();
bool loadVerifyLocations(
const std::string &caFile,
const std::string &caPath
);
bool loadCert(
const std::string &certificateChainFile,
const std::string &privateKeyFile
);
void setCipherList();
};
#endif // SUPPORT_DTLS
#endif

View File

@ -52,9 +52,12 @@ public:
// inherited from Destination<T>
virtual void receive(T element)
{
printf("Adapter::receive\n");
Source<T>::send(element);
}
virtual void notifyQueueRunning() {
Source<T>::sendQueueRunningNotification();
}
};
#endif /*ADAPTER_H_*/

View File

@ -45,7 +45,7 @@ public:
*/
std::string get(const std::string& name, XMLElement* elem = NULL) throw(IllegalEntry);
/** searches for a optinal config entry, returns the emtpy string of not found
/** searches for an optinal config entry, returns the emtpy string if not found
* @param name the name of the element
* @param elem the XMLElement we want to start the search, default is the root of the node
*/

View File

@ -168,9 +168,11 @@ private:
// allow onTimeout to call addTimeout(...)
mutex.unlock();
// Unlocking the mutex should do no harm
// because onTimeout should not call any method
// that invalidates our iterator.
te->n->onTimeout(te->dataPtr);
mutex.lock();
Source<T>::atomicRelease();
iter = timeouts.erase(iter);
@ -203,7 +205,8 @@ private:
T element;
Module::registerCurrentThread();
Source<T>::sendQueueRunningNotification();
while (true) {
if (Module::getExitFlag()) {
if (!Module::getShutdownProperly()) break;

View File

@ -54,6 +54,11 @@ public:
// the packets in an own thread; for now, I think this is not needed
process(packet);
}
virtual void sendQueueRunningNotification() {
for (size_t i = 0; i < size; i++) {
destinations[i]->notifyQueueRunning();
}
}
private:
inline void process(T packet)

View File

@ -21,6 +21,9 @@ public:
virtual ~Destination() { };
virtual void receive(T e) = 0;
// See Source.h for comments on the queue running notification
virtual void notifyQueueRunning() {}
};
template<>
@ -35,6 +38,12 @@ public:
{
THROWEXCEPTION("this module is no destination!");
}
// See Source.h for comments on the Start Signal
virtual void notifyQueueRunning()
{
THROWEXCEPTION("this module is no destination!");
}
};

View File

@ -33,17 +33,20 @@ Graph::Graph() : reserved(30)
Graph::~Graph()
{
DPRINTF("~Graph()");
std::vector<CfgNode*> ts = topoSort();
for (size_t i = 0; i < reserved; i++) {
for (size_t j = 0; j < nodes.size(); j++) {
if (matrix[i][j] != NULL) // free all edges
delete matrix[i][j];
matrix[i][j] = NULL;
}
delete [] matrix[i];
matrix[i] = NULL;
}
delete [] matrix;
matrix = NULL;
// we delete in topological order so that we never get into a race
// where a module (which is deleted by the Cfg) is still running and wants to
// send data to an module which got already freed. We rely on the d'tors to do
@ -51,6 +54,7 @@ Graph::~Graph()
for (size_t i = 0; i < ts.size(); i++) {
delete ts[i];
}
DPRINTF("~Graph() done");
}
CfgNode* Graph::addNode(Cfg* cfg)

View File

@ -106,6 +106,23 @@ public:
return true;
}
// Subsequent modules that do not have
// their own timer will be informed about the fact that
// the queue is now running. It was added to inform
// subsequent modules that
// they can start using its predecessor's timer.
inline void sendQueueRunningNotification() {
bool c = false;
mutex.lock();
if (isConnected()) {
dest->notifyQueueRunning();
c = true;
}
mutex.unlock();
// Unless c == true something went badly wrong
if (!c) THROWEXCEPTION("sendQueueRunningNotification() called although not connected.");
}
inline uint32_t atomicLock()
{
return atomic_lock(&syncLock);
@ -127,5 +144,7 @@ private:
Destination<T>* dest;
};
template <> inline void Source<NullEmitable *>::sendQueueRunningNotification() { }
#endif

View File

@ -72,6 +72,8 @@ ADD_LIBRARY(modules
ipfix/Connection.cpp
ipfix/IpfixReceiverUdpIpV4.cpp
ipfix/IpfixReceiverSctpIpV4.cpp
ipfix/IpfixReceiverDtlsUdpIpV4.cpp
ipfix/IpfixReceiverDtlsSctpIpV4.cpp
ipfix/IpfixReceiverFile.cpp
ipfix/IpfixReceiverFileCfg.cpp
ipfix/IpfixRawdirReader.cpp

View File

@ -40,9 +40,7 @@ public:
virtual std::string getName()
{
// see below why this hack is needed
T t;
return get_name(t);
return "";
}
virtual QueueCfg<T>* create(XMLElement* e) {
@ -94,20 +92,13 @@ class IpfixRecord;
typedef QueueCfg<Packet*> PacketQueueCfg;
typedef QueueCfg<IpfixRecord*> IpfixQueueCfg;
// this hack with template specialization is needed because
// gcc (GCC) 4.1.3 20070812 (prerelease) (Debian 4.1.2-15)
// has in ICE if I used a static variable for the name
template<typename X> inline std::string get_name(const X x)
{
return "";
}
template<> inline std::string get_name<Packet*>(Packet* x)
template <> std::string QueueCfg<Packet*>::getName()
{
return "packetQueue";
}
template<> inline std::string get_name<IpfixRecord*>(IpfixRecord* x)
template <> std::string QueueCfg<IpfixRecord*>::getName()
{
return "ipfixQueue";
}

View File

@ -23,15 +23,22 @@
#include "core/Cfg.h"
#include <modules/ipfix/IpfixCollectorCfg.h>
#include <modules/ipfix/IpfixReceiverUdpIpV4.hpp>
#include <modules/ipfix/IpfixReceiverSctpIpV4.hpp>
#include <modules/ipfix/IpfixReceiverDtlsUdpIpV4.hpp>
#include <modules/ipfix/IpfixReceiverDtlsSctpIpV4.hpp>
#include <modules/ipfix/IpfixReceiverFile.hpp>
#include <common/ipfixlolib/ipfixlolib.h>
#include <string>
#include <set>
/**
* This class holds the <collector> ... </collector> information of the config
* This class holds the <collector> ... </collector> and the <listener> ...
* </listener> information of the config
*/
class CollectorCfg
{
@ -39,8 +46,9 @@ public:
std::string getName() { return "collector"; }
CollectorCfg(XMLElement* elem)
: protocol(0), port(4739)
: protocol(UDP), port(0), mtu(0)
{
uint16_t defaultPort = 4739;
if (!elem)
return;
@ -54,29 +62,57 @@ public:
authorizedHosts.push_back(e->getContent());
} else if (e->matches("transportProtocol")) {
std::string prot = e->getContent();
if (prot=="17" || prot=="UDP")
protocol = 17;
else if (prot=="132" || prot=="SCTP")
protocol = 132;
else
if (prot=="17" || prot=="UDP") {
protocol = UDP;
defaultPort = 4739;
} else if (prot=="132" || prot=="SCTP") {
protocol = SCTP;
defaultPort = 4739;
} else if (prot=="DTLS_OVER_UDP") {
protocol = DTLS_OVER_UDP;
defaultPort = 4740;
} else if (prot=="DTLS_OVER_SCTP") {
protocol = DTLS_OVER_SCTP;
defaultPort = 4740;
} else
THROWEXCEPTION("Invalid configuration parameter for transportProtocol (%s)", prot.c_str());
} else if (e->matches("port")) {
port = (uint16_t)atoi(e->getContent().c_str());
if (port == 0)
THROWEXCEPTION("Invalid configuration parameter for port (%u)", port);
} else if (e->matches("mtu")) {
mtu = (uint16_t)atoi(e->getContent().c_str());
} else if (e->matches("peerFqdn")) {
string strdnsname(e->getFirstText());
transform(strdnsname.begin(),strdnsname.end(),strdnsname.begin(),
::tolower);
peerFqdns.insert(strdnsname);
} else {
msg(MSG_FATAL, "Unknown collector config statement %s\n", e->getName().c_str());
continue;
}
}
if (port==0) port = defaultPort;
}
IpfixReceiver* createIpfixReceiver() {
IpfixReceiver* createIpfixReceiver(
const std::string &certificateChainFile,
const std::string &privateKeyFile,
const std::string &caFile,
const std::string &caPath) {
IpfixReceiver* ipfixReceiver;
if (protocol == 132)
ipfixReceiver = new IpfixReceiverSctpIpV4(port, ipAddress);
else
ipfixReceiver = new IpfixReceiverUdpIpV4(port, ipAddress);
if (protocol == SCTP)
ipfixReceiver = new IpfixReceiverSctpIpV4(port, ipAddress);
else if (protocol == DTLS_OVER_UDP)
ipfixReceiver = new IpfixReceiverDtlsUdpIpV4(port,
ipAddress, certificateChainFile,
privateKeyFile, caFile, caPath, peerFqdns);
else if (protocol == DTLS_OVER_SCTP)
ipfixReceiver = new IpfixReceiverDtlsSctpIpV4(port,
ipAddress, certificateChainFile,
privateKeyFile, caFile, caPath, peerFqdns);
else
ipfixReceiver = new IpfixReceiverUdpIpV4(port, ipAddress);
if (!ipfixReceiver) {
THROWEXCEPTION("Could not create IpfixReceiver");
@ -95,6 +131,8 @@ public:
//(ipAddressType == other->ipAddressType) &&
(protocol == other->protocol) &&
(port == other->port) &&
(mtu == other->mtu) &&
(peerFqdns == other->peerFqdns) &&
(authorizedHosts.size() == other->authorizedHosts.size())) {
for (uint16_t i = 0; i < authorizedHosts.size(); i++)
if (authorizedHosts[i] != other->authorizedHosts[i])
@ -105,16 +143,20 @@ public:
return false;
}
std::set<std::string> getPeerFqdns() { return peerFqdns; }
std::string getIpAddress() { return ipAddress; }
//unsigned getIpAddressType() { return ipAddressType; }
uint16_t getProtocol() { return protocol; }
ipfix_transport_protocol getProtocol() { return protocol; }
uint16_t getPort() { return port; }
uint16_t getMtu() { return mtu; }
private:
std::string ipAddress;
std::vector<std::string> authorizedHosts;
uint16_t protocol;
ipfix_transport_protocol protocol;
uint16_t port;
uint16_t mtu;
std::set<std::string> peerFqdns;
};
#endif /*COLLECTORCFG_H_*/

View File

@ -19,6 +19,11 @@
*/
#include "IpfixCollectorCfg.h"
#include "CollectorCfg.h"
#include <modules/ipfix/IpfixReceiverUdpIpV4.hpp>
#include <modules/ipfix/IpfixReceiverDtlsUdpIpV4.hpp>
#include <modules/ipfix/IpfixReceiverSctpIpV4.hpp>
#include <modules/ipfix/IpfixReceiverDtlsSctpIpV4.hpp>
IpfixCollectorCfg::IpfixCollectorCfg(XMLElement* elem)
: CfgHelper<IpfixCollector, IpfixCollectorCfg>(elem, "ipfixCollector"),
@ -30,6 +35,14 @@ IpfixCollectorCfg::IpfixCollectorCfg(XMLElement* elem)
msg(MSG_INFO, "IpfixCollectorCfg: Start reading ipfixCollector section");
udpTemplateLifetime = getInt("udpTemplateLifetime", -1);
// Config for DTLS
certificateChainFile = getOptional("cert");
privateKeyFile = getOptional("key");
caFile = getOptional("CAfile");
caPath = getOptional("CApath");
// observationDomainId = getInt("observationDomainId", 0);
XMLNode::XMLSet<XMLElement*> set = elem->getElementChildren();
for (XMLNode::XMLSet<XMLElement*>::iterator it = set.begin();
it != set.end();
@ -37,9 +50,18 @@ IpfixCollectorCfg::IpfixCollectorCfg(XMLElement* elem)
XMLElement* e = *it;
if (e->matches("listener")) {
if (listener)
THROWEXCEPTION("listener already set. There can only be one <listener> Element per Collector.");
listener = new CollectorCfg(e);
if (listener->getMtu() != 0) {
delete listener;
THROWEXCEPTION("You can not set the MTU for a listener.");
}
} else if (e->matches("udpTemplateLifetime")) { // already done
} else if (e->matches("next")) { // ignore next
} else if (e->matches("cert") || e->matches("key") ||
e->matches("CAfile") || e->matches("CApath")) {
// already done!
} else {
msg(MSG_FATAL, "Unkown observer config statement %s\n", e->getName().c_str());
continue;
@ -49,11 +71,18 @@ IpfixCollectorCfg::IpfixCollectorCfg(XMLElement* elem)
if (listener == NULL)
THROWEXCEPTION("collectingProcess has to listen on one address!");
if (listener->getProtocol() != UDP &&
listener->getProtocol() != SCTP &&
listener->getProtocol() != DTLS_OVER_UDP &&
listener->getProtocol() != DTLS_OVER_SCTP)
THROWEXCEPTION("collectingProcess can handle only UDP or SCTP!");
msg(MSG_INFO, "IpfixCollectorCfg: Successfully parsed collectingProcess section");
}
IpfixCollectorCfg::~IpfixCollectorCfg()
{
// FIXME: Shouldn't we delete listener here?
}
IpfixCollectorCfg* IpfixCollectorCfg::create(XMLElement* elem)
@ -65,7 +94,7 @@ IpfixCollectorCfg* IpfixCollectorCfg::create(XMLElement* elem)
IpfixCollector* IpfixCollectorCfg::createInstance()
{
instance = new IpfixCollector(listener->createIpfixReceiver());
instance = new IpfixCollector(listener->createIpfixReceiver(certificateChainFile, privateKeyFile, caFile, caPath));
if(udpTemplateLifetime>=0)
instance->setTemplateLifetime((uint16_t)udpTemplateLifetime);
return instance;

View File

@ -22,9 +22,10 @@
#define IPFIXCOLLECTORCFG_H_
#include "core/Cfg.h"
#include "CollectorCfg.h"
#include <modules/ipfix/IpfixCollector.hpp>
class CollectorCfg;
class IpfixCollectorCfg
: public CfgHelper<IpfixCollector, IpfixCollectorCfg>
{
@ -40,6 +41,15 @@ public:
private:
CollectorCfg* listener;
friend class CollectorCfg;
/** DTLS parameters */
std::string certificateChainFile;
std::string privateKeyFile;
std::string caFile;
std::string caPath;
IpfixCollector* ipfixCollector;

View File

@ -22,10 +22,10 @@
IpfixExporterCfg::IpfixExporterCfg(XMLElement* elem)
: CfgHelper<IpfixSender, IpfixExporterCfg>(elem, "ipfixExporter"),
templateRefreshTime(IS_DEFAULT_TEMPLATE_TIMEINTERVAL), templateRefreshRate(0),
templateRefreshTime(IS_DEFAULT_TEMPLATE_TIMEINTERVAL), /* templateRefreshRate(0), */
sctpDataLifetime(0), sctpReconnectInterval(0),
maxPacketSize(0), exportDelay(0),
recordRateLimit(0), observationDomainId(0)
recordRateLimit(0), observationDomainId(0),
dtlsMaxConnectionLifetime(0)
{
if (!elem) {
@ -33,12 +33,18 @@ IpfixExporterCfg::IpfixExporterCfg(XMLElement* elem)
}
recordRateLimit = getInt("maxRecordRate", IS_DEFAULT_MAXRECORDRATE);
observationDomainId = getInt("observationDomainId", 0);
msg(MSG_INFO, "Exporter: using maximum rate of %d records/second", recordRateLimit);
observationDomainId = getInt("observationDomainId", 0);
sctpDataLifetime = getTimeInUnit("sctpDataLifetime", mSEC, IS_DEFAULT_SCTP_DATALIFETIME);
sctpReconnectInterval = getTimeInUnit("sctpReconnectInterval", SEC, IS_DEFAULT_SCTP_RECONNECTINTERVAL);
templateRefreshRate = getInt("templateRefreshRate", IS_DEFAULT_TEMPLATE_RECORDINTERVAL);
/* templateRefreshRate = getInt("templateRefreshRate", IS_DEFAULT_TEMPLATE_RECORDINTERVAL); */
templateRefreshTime = getTimeInUnit("templateRefreshInterval", SEC, IS_DEFAULT_TEMPLATE_TIMEINTERVAL);
// Config for DTLS
certificateChainFile = getOptional("cert");
privateKeyFile = getOptional("key");
caFile = getOptional("CAfile");
caPath = getOptional("CApath");
dtlsMaxConnectionLifetime = getTimeInUnit("dtlsMaxConnectionLifetime", SEC, IS_DEFAULT_DTLS_CONNECTIONLIFETIME);
XMLNode::XMLSet<XMLElement*> set = elem->getElementChildren();
@ -48,9 +54,24 @@ IpfixExporterCfg::IpfixExporterCfg(XMLElement* elem)
XMLElement* e = *it;
if (e->matches("collector")) {
collectors.push_back(new CollectorCfg(e));
} else if (e->matches("maxRecordRate") || e->matches("sctpDataLifetime") || e->matches("sctpReconnectInterval")
|| e->matches("templateRefreshRate")|| e->matches("templateRefreshInterval") || e->matches("observationDomainId")) {
CollectorCfg *c = new CollectorCfg(e);
if (c->getPeerFqdns().size() > 1) {
delete c;
THROWEXCEPTION("You specified more than one peerFqdn for an exporter.");
}
collectors.push_back(c);
} else if ( e->matches("maxRecordRate") ||
e->matches("sctpDataLifetime") ||
e->matches("sctpReconnectInterval") ||
/* e->matches("templateRefreshRate") || */
e->matches("templateRefreshInterval") ||
e->matches("observationDomainId") ||
e->matches("cert") ||
e->matches("key") ||
e->matches("CAfile") ||
e->matches("CApath") ||
e->matches("dtlsMaxConnectionLifetime")
) {
// already done!
} else {
THROWEXCEPTION("Illegal Exporter config entry \"%s\" found",
@ -68,14 +89,69 @@ IpfixExporterCfg::~IpfixExporterCfg()
IpfixSender* IpfixExporterCfg::createInstance()
{
instance = new IpfixSender(observationDomainId, recordRateLimit, sctpDataLifetime,
sctpReconnectInterval, templateRefreshTime, templateRefreshRate);
sctpReconnectInterval, templateRefreshTime,
certificateChainFile, privateKeyFile, caFile, caPath);
for (unsigned i = 0; i != collectors.size(); ++i) {
std::vector<CollectorCfg*>::const_iterator it;
for (it = collectors.begin(); it != collectors.end(); it++) {
CollectorCfg *p = *it;
#ifdef DEBUG
const char *protocol;
switch (p->getProtocol()) {
case SCTP:
protocol = "SCTP"; break;
case DTLS_OVER_UDP:
protocol = "DTLS_OVER_UDP"; break;
case DTLS_OVER_SCTP:
protocol = "DTLS_OVER_SCTP"; break;
case UDP:
protocol = "UDP"; break;
default:
protocol = "unknown protocol"; break;
}
msg(MSG_DEBUG, "IpfixExporter: adding collector %s://%s:%d",
collectors[i]->getProtocol()==132?"SCTP":"UDP",
collectors[i]->getIpAddress().c_str(),
collectors[i]->getPort());
instance->addCollector(collectors[i]->getIpAddress().c_str(), collectors[i]->getPort(), collectors[i]->getProtocol());
protocol,
p->getIpAddress().c_str(),
p->getPort());
#endif
void *aux_config = NULL;
ipfix_aux_config_dtls_over_udp acdou;
ipfix_aux_config_dtls_over_sctp acdos;
ipfix_aux_config_udp acu;
ipfix_aux_config_udp *pacu = NULL;
ipfix_aux_config_dtls *pacd = NULL;
switch (p->getProtocol()) {
case DTLS_OVER_UDP:
acdou.max_connection_lifetime = dtlsMaxConnectionLifetime;
pacd = &acdou.dtls;
pacu = &acu;
aux_config = &acdou;
break;
case DTLS_OVER_SCTP:
pacd = &acdos.dtls;
aux_config = &acdos;
break;
case UDP:
aux_config = &acu;
pacu = &acu;
break;
default:
break;
}
if (pacd) {
pacd->peer_fqdn = NULL;
const std::set<std::string> peerFqdns = p->getPeerFqdns();
std::set<std::string>::const_iterator it = peerFqdns.begin();
if (it != peerFqdns.end())
pacd->peer_fqdn = it->c_str();
}
if (pacu) {
pacu->mtu = p->getMtu();
}
instance->addCollector(
p->getIpAddress().c_str(),
p->getPort(), p->getProtocol(),
aux_config);
}
return instance;
@ -95,10 +171,8 @@ bool IpfixExporterCfg::deriveFrom(IpfixExporterCfg* other)
bool IpfixExporterCfg::equalTo(IpfixExporterCfg* other)
{
if (maxPacketSize != other->maxPacketSize) return false;
if (exportDelay != other->exportDelay) return false;
if (templateRefreshTime != other->templateRefreshTime) return false;
if (templateRefreshRate != other->templateRefreshRate) return false;
/* if (templateRefreshRate != other->templateRefreshRate) return false; */ /* TODO */
if (collectors.size() != other->collectors.size()) return false;
std::vector<CollectorCfg*>::const_iterator iter = collectors.begin();
while (iter != collectors.end()) {

View File

@ -49,21 +49,22 @@ private:
/** template management */
unsigned templateRefreshTime;
unsigned templateRefreshRate;
/* unsigned templateRefreshRate; */ /* TODO */
/** sctp parameters */
uint32_t sctpDataLifetime;
uint32_t sctpReconnectInterval;
/** packet restrictions */
uint16_t maxPacketSize;
unsigned exportDelay;
uint32_t recordRateLimit;
uint32_t observationDomainId;
int recordsPerPacket;
int recordLength;
/** DTLS parameters */
std::string certificateChainFile;
std::string privateKeyFile;
std::string caFile;
std::string caPath;
unsigned dtlsMaxConnectionLifetime;
};
#endif /*IPFIXEXPORTERCFG_H_*/

View File

@ -69,7 +69,7 @@ int IpfixFileWriter::addCollector(uint16_t observationDomainId, std::string file
msg(MSG_ERROR,
"maximum filsize < maximum message length - this could lead to serious problems");
if(ipfix_add_collector(ex, my_filename.c_str(), maximumFilesize, DATAFILE) != 0) {
if(ipfix_add_collector(ex, my_filename.c_str(), maximumFilesize, DATAFILE, NULL) != 0) {
msg(MSG_FATAL, "IpfixFileWriter: ipfix_add_collector of %s failed", my_filename.c_str());
return -1;
}

View File

@ -426,7 +426,6 @@ void IpfixPrinter::onTemplateDestruction(IpfixTemplateDestructionRecord* record)
}
/**
* prints a datarecord in a special, easy-to-read data format in one line
*/

View File

@ -42,6 +42,14 @@ IpfixReceiver::IpfixReceiver()
{
}
IpfixReceiver::IpfixReceiver(int port)
: exitFlag(true),
receiverPort(port),
thread(threadWrapper)
{
}
/**
* Frees memory used by an IpfixReceiver.
*/

View File

@ -40,6 +40,7 @@ class IpfixReceiver
{
public:
IpfixReceiver();
IpfixReceiver(int port);
virtual ~IpfixReceiver();
void performStart();

View File

@ -0,0 +1,366 @@
/* vim: set sts=4 sw=4 cindent nowrap: This modeline was added by Daniel Mentz */
/*
* IPFIX Concentrator Module Library - SCTP Receiver
* Copyright (C) 2007 Alex Melnik
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifdef SUPPORT_DTLS_OVER_SCTP
#include "ipfixlolib/ipfixlolib.h"
#include "IpfixReceiverDtlsSctpIpV4.hpp"
#include "IpfixPacketProcessor.hpp"
#include "IpfixParser.hpp"
#include "ipfix.hpp"
#include "common/msg.h"
#include <stdexcept>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include <sstream>
/**
* Does DTLS over SCTP/IPv4 specific initialization.
* @param port Port to listen on
*/
IpfixReceiverDtlsSctpIpV4::IpfixReceiverDtlsSctpIpV4(int port, const std::string ipAddr,
const std::string &certificateChainFile, const std::string &privateKeyFile,
const std::string &caFile, const std::string &caPath,
const std::set<string> &peerFqdnsParam)
: IpfixReceiver(port),ssl_ctx(certificateChainFile,privateKeyFile,caFile,caPath,
! peerFqdnsParam.empty()),listen_socket(-1),maxfd(0),
peerFqdns(peerFqdnsParam), statReceivedPackets(0)
{
struct sockaddr_in serverAddress;
struct sctp_event_subscribe ses;
int flags;
/* FIXME: make error messages consistent with IpfixReceiverDtlsUdpIpV4 */
try {
listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);
if(listen_socket < 0) {
/* FIXME: should we use strerror_r? */
msg(MSG_FATAL, "socket creation failed: %s", strerror(errno));
THROWEXCEPTION("Cannot create IpfixReceiverDtlsSctpIpV4, socket creation failed: %s", strerror(errno));
}
/* set socket to non-blocking i/o */
flags = fcntl(listen_socket,F_GETFL,0);
flags |= O_NONBLOCK;
if (fcntl(listen_socket,F_SETFL,flags)<0) {
THROWEXCEPTION("IPFIX: Failed to set socket to non-blocking i/o");
}
// if ipAddr set: Bind a specific IP address to our socket.
// else: use wildcard address
if(ipAddr.empty())
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
else
if ( ! inet_pton(AF_INET,ipAddr.c_str(),&serverAddress.sin_addr) ) {
THROWEXCEPTION("IP address invalid.");
}
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(port);
if(bind(listen_socket, (struct sockaddr*)&serverAddress,
sizeof(struct sockaddr_in)) < 0) {
msg(MSG_FATAL, "Cannot bind socket: %s", strerror(errno));
THROWEXCEPTION("Cannot create IpfixReceiverDtlsSctpIpV4 %s:%d",ipAddr.c_str(), port );
}
if(listen(listen_socket, SCTP_MAX_BACKLOG) < 0 ) {
msg(MSG_FATAL, "Can not listen socket: %s", strerror(errno));
THROWEXCEPTION("Cannot create IpfixReceiverDtlsSctpIpV4 %s:%d",ipAddr.c_str(), port );
}
msg(MSG_INFO, "SCTP Receiver listening on %s:%d, FD=%d", (ipAddr == "")?std::string("ALL").c_str() : ipAddr.c_str(),
port,
listen_socket);
memset(&ses, 0, sizeof(ses));
ses.sctp_data_io_event = 1;
if ( setsockopt(listen_socket, IPPROTO_SCTP, SCTP_EVENTS, &ses, sizeof(ses))) {
msg(MSG_FATAL, "setsockopt() failed: %s", strerror(errno));
THROWEXCEPTION("Cannot create IpfixReceiverDtlsSctpIpV4 %s:%d",ipAddr.c_str(), port );
}
/* BIO_new_dgram_sctp sets the necessary socket options on listen_socket.
* This includes activating SCTP-AUTH and subscribing to all necessary events.
* We can delete this BIO right after because we're only calling it for the
* side effects on listen_socket. */
BIO_free(BIO_new_dgram_sctp(listen_socket, BIO_NOCLOSE));
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_SET(listen_socket,&readfds);
update_maxfd();
/* TODO: Find out what this is? */
SensorManager::getInstance().addSensor(this, "IpfixReceiverDtlsSctpIpV4", 0);
msg(MSG_INFO, "DTLS over SCTP Receiver listening on %s:%d, FD=%d", (ipAddr == "")?std::string("ALL").c_str() : ipAddr.c_str(),
port,
listen_socket);
} catch(...) {
if (listen_socket>=0) {
close(listen_socket);
listen_socket = -1;
}
throw; // rethrow
}
}
/**
* Does SCTP/IPv4 specific cleanup
*/
IpfixReceiverDtlsSctpIpV4::~IpfixReceiverDtlsSctpIpV4() {
close(listen_socket);
}
int verify_peer_cb_sctp(void *context, const char *dnsname) {
IpfixReceiverDtlsSctpIpV4 *receiver =
static_cast<IpfixReceiverDtlsSctpIpV4 *>(context);
string strdnsname(dnsname);
transform(strdnsname.begin(),strdnsname.end(),strdnsname.begin(),
::tolower);
if (receiver->peerFqdns.find(strdnsname)!=receiver->peerFqdns.end())
return 1;
else
return 0;
}
/**
* DTLS over SCTP specific listener function. This function is called by @c listenerThread()
*/
void IpfixReceiverDtlsSctpIpV4::run() {
struct sockaddr_in clientAddress;
socklen_t clientAddressLen = sizeof(struct sockaddr_in);
int ret;
int rfd;
struct timespec timeOut;
/* set a 400ms time-out on the pselect */
timeOut.tv_sec = 0L;
timeOut.tv_nsec = 400000000L;
while(!exitFlag) {
fd_set tmpreadfds = readfds;
fd_set tmpwritefds = writefds;
ret = pselect(maxfd + 1, &tmpreadfds, &tmpwritefds, NULL, &timeOut, NULL);
if (ret == 0) {
/* Timeout */
continue;
}
if ((ret == -1) && (errno == EINTR)) {
DPRINTF("select() returned due to signal");
/* There was a signal... ignore */
continue;
}
if (ret < 0) {
msg(MSG_ERROR ,"select() returned with an error: %s",strerror(errno));
THROWEXCEPTION("IpfixReceiverDtlsSctpIpV4: terminating listener thread");
break;
}
DPRINTF("select() returned %d",ret);
// looking for a new client to connect at listen_socket
if (FD_ISSET(listen_socket, &tmpreadfds)){
rfd = accept(listen_socket, (struct sockaddr*)&clientAddress, &clientAddressLen);
if (rfd >= 0){
if ( ! isHostAuthorized(&clientAddress.sin_addr,
sizeof(clientAddress.sin_addr))) {
/* Do not accept connections from unauthorized hosts. */
close(rfd);
} else {
msg(MSG_DEBUG, "IpfixReceiverDtlsSctpIpV4: Client connected from %s:%d, FD=%d", inet_ntoa(clientAddress.sin_addr), ntohs(clientAddress.sin_port), rfd);
DtlsConnectionPtr conn = DtlsConnectionPtr( new DtlsConnection(*this,&clientAddress,rfd));
connections.insert(make_pair(rfd,conn));
update_maxfd();
}
}else{
msg(MSG_ERROR ,"accept() in ipfixReceiver failed");
/* TODO: Don't throw an exception here. */
THROWEXCEPTION("IpfixReceiverDtlsSctpIpV4: unable to accept new connection");
}
}
// check all connected sockets for new available data
for (rfd = 0; rfd <= maxfd; ++rfd) {
if (rfd == listen_socket) continue;
if (FD_ISSET(rfd, &readfds) || FD_ISSET(rfd, &writefds)) {
if (FD_ISSET(rfd, &readfds)) DPRINTF("descriptor %d is ready for reading",rfd);
if (FD_ISSET(rfd, &writefds)) DPRINTF("descriptor %d is ready for writing",rfd);
connections_map::iterator it = connections.find(rfd);
if (it == connections.end()) {
/* This should not happend. */
msg(MSG_ERROR,"Can't find connection for file descriptor.");
FD_CLR(rfd,&readfds);
FD_CLR(rfd,&writefds);
continue;
}
ret = it->second->fdready();
if (ret == 0) {
DPRINTF("fdready() returned 0. Deleting connection.");
remove_connection(rfd);
update_maxfd();
}
}
}
}
msg(MSG_DEBUG, "IpfixReceiverDtlsSctpIpV4: Exiting");
}
/**
* statistics function called by StatisticsManager
*/
std::string IpfixReceiverDtlsSctpIpV4::getStatisticsXML(double interval)
{
ostringstream oss;
oss << "<receivedPackets>" << statReceivedPackets << "</receivedPackets>" << endl;
return oss.str();
}
void IpfixReceiverDtlsSctpIpV4::remove_connection(int socket) {
connections.erase(socket);
}
void IpfixReceiverDtlsSctpIpV4::update_maxfd(void) {
maxfd = 0;
if (listen_socket>=0) maxfd = listen_socket;
if (!connections.empty() && connections.rbegin()->first > maxfd)
maxfd = connections.rbegin()->first;
}
IpfixReceiverDtlsSctpIpV4::DtlsConnection::DtlsConnection(IpfixReceiverDtlsSctpIpV4 &_parent,struct sockaddr_in *pclientAddress,int _socket) :
clientAddress(*pclientAddress), socket(_socket), parent(_parent),
last_used(time(NULL)), sourceID(new IpfixRecord::SourceID) {
memcpy(sourceID->exporterAddress.ip, &clientAddress.sin_addr.s_addr, 4);
sourceID->exporterAddress.len = 4;
sourceID->exporterPort = ntohs(clientAddress.sin_port);
sourceID->protocol = IPFIX_protocolIdentifier_SCTP;
sourceID->receiverPort = parent.receiverPort;
sourceID->fileDescriptor = socket;
ssl = parent.ssl_ctx.SSL_new();
BIO *bio;
/* create output abstraction for SSL object */
bio = BIO_new_dgram_sctp(socket,BIO_NOCLOSE);
BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_CONNECTED, 0, &clientAddress);
SSL_set_bio(ssl,bio,bio);
SSL_set_accept_state(ssl);
vpcd.cb = &verify_peer_cb_sctp;
vpcd.context = &parent;
SSL_set_ex_data(ssl,get_openssl_ex_data_idx_vpcd(),&vpcd);
/* Pretend that the file description is ready */
fdready();
}
IpfixReceiverDtlsSctpIpV4::DtlsConnection::~DtlsConnection() {
DPRINTF("~DtlsConnection()");
shutdown();
}
/* Return values:
* 1 Success
* 0 Failure. Remove this connection
*/
int IpfixReceiverDtlsSctpIpV4::DtlsConnection::fdready() {
int ret, error;
boost::shared_array<uint8_t> data(new uint8_t[MAX_MSG_LEN]);
if (socket < 0) return 0;
ret = SSL_read(ssl,data.get(),MAX_MSG_LEN);
error = SSL_get_error(ssl,ret);
#ifdef DEBUG
msg_openssl_return_code(MSG_DEBUG,"SSL_read()",ret,error);
DPRINTF("Error: %s",strerror(errno));
DPRINTF("Received shutdown: %s",SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN ? "yes":"no");
#endif
if (ret<0) {
if (error == SSL_ERROR_WANT_READ) {
FD_SET(socket,&parent.readfds);
FD_CLR(socket,&parent.writefds);
return 1;
} else if (error == SSL_ERROR_WANT_WRITE) {
FD_SET(socket,&parent.writefds);
FD_CLR(socket,&parent.readfds);
return 1;
}
msg_openssl_return_code(MSG_ERROR,"SSL_read()",ret,error);
msg_openssl_errors();
shutdown();
return 0;
} else if (ret==0) {
if (error == SSL_ERROR_ZERO_RETURN) {
// remote side closed connection
DPRINTF("remote side closed connection.");
} else {
msg_openssl_return_code(MSG_ERROR,"SSL_read()",ret,error);
msg_openssl_errors();
}
shutdown();
return 0;
} else {
DPRINTF("SSL_read() returned %d bytes.",ret);
}
parent.statReceivedPackets++;
parent.mutex.lock();
for (std::list<IpfixPacketProcessor*>::iterator i = parent.packetProcessors.begin();
i != parent.packetProcessors.end(); ++i) {
(*i)->processPacket(data, ret, sourceID);
}
parent.mutex.unlock();
return 1;
}
void IpfixReceiverDtlsSctpIpV4::DtlsConnection::shutdown() {
int ret, error;
if (!ssl) return;
ret = SSL_shutdown(ssl);
if (ret == 0) {
DPRINTF("Calling SSL_shutdown a second time.");
ret = SSL_shutdown(ssl);
}
error = SSL_get_error(ssl,ret);
#if DEBUG
msg_openssl_return_code(MSG_DEBUG,"SSL_shutdown()",ret,error);
#endif
DPRINTF("SSL_free(ssl)");
parent.ssl_ctx.SSL_free(ssl);
ssl = NULL;
FD_CLR(socket,&parent.readfds);
FD_CLR(socket,&parent.writefds);
close(socket);
socket = -1;
}
#endif /*SUPPORT_SCTP*/

View File

@ -0,0 +1,140 @@
/* vim: set sts=4 sw=4 cindent nowrap: This modeline was added by Daniel Mentz */
/*
* IPFIX Concentrator Module Library - SCTP Receiver
* Copyright (C) 2007 Alex Melnik
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef _IPFIX_RECEIVER_DTLSSCTPIPV4_H_
#define _IPFIX_RECEIVER_DTLSSCTPIPV4_H_
#include <pthread.h>
#include <stdint.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <list>
#include <map>
#include <set>
#include "IpfixReceiver.hpp"
#include "IpfixPacketProcessor.hpp"
#ifdef SUPPORT_DTLS
#include "common/openssl/SSLCTXWrapper.hpp"
#include <openssl/ssl.h>
#ifndef HEADER_DH_H
#include <openssl/dh.h>
#endif
#endif
// Quote from man page: "maximum length to which the queue of pending connections
// for sockfd may grow. If a connection request arrives when the queue is
// full, the client may receive an error with an indication of ECONNREFUSED
// or, if the underlying protocol supports retransmission, the request may
// be ignored so that a later reattempt at connection succeeds."
#define SCTP_MAX_BACKLOG 5
class IpfixReceiverDtlsSctpIpV4 : public IpfixReceiver, Sensor {
#ifdef SUPPORT_DTLS_OVER_SCTP
public:
IpfixReceiverDtlsSctpIpV4(int port, const std::string ipAddr = "",
const std::string &certificateChainFile = "", const std::string &privateKeyFile = "",
const std::string &caFile = "", const std::string &caPath = "",
const std::set<string> &peerFqdns = std::set<string>() );
virtual ~IpfixReceiverDtlsSctpIpV4();
virtual void run();
virtual std::string getStatisticsXML(double interval);
private:
/* It is utterly important that ssl_ctx is defined
* before connections_map!.
* The reason is that the SSL objects depend on the SSL_CTX object.
* There's a dependency between SSL_CTX and SSL objects.
* The SSL_CTX object is created first. All the SSL objects
* that derive from that SSL_CTX are created by calling
* ssl = SSL_new(ctx). It's important to call SSL_free(ssl) before
* calling SSL_CTX_free(ctx).
* The descructor of DtlsConnection calls SSL_free(ssl) whereas
* the descructor of SSL_CTX_wrapper calls SSL_CTX_free(ctx).
* Also note, that the descructors are called in the opposite order
* than the correspondig members are defined here.*/
SSL_CTX_wrapper ssl_ctx;
int listen_socket;
fd_set readfds, writefds;
int maxfd;
const std::set<string> peerFqdns;
friend int verify_peer_cb_sctp(void *context, const char *dnsname);
uint32_t statReceivedPackets; /**< number of received packets */
class DtlsConnection {
public:
DtlsConnection(IpfixReceiverDtlsSctpIpV4 &parent,struct sockaddr_in *clientAddress, int socket);
~DtlsConnection();
int fdready();
private:
struct sockaddr_in clientAddress;
int socket;
IpfixReceiverDtlsSctpIpV4 &parent;
SSL* ssl;
time_t last_used;
int accept();
void shutdown();
int verify_peer();
struct verify_peer_cb_data vpcd;
boost::shared_ptr<IpfixRecord::SourceID> sourceID;
};
typedef boost::shared_ptr<DtlsConnection> DtlsConnectionPtr;
typedef std::map<int,DtlsConnectionPtr> connections_map;
connections_map connections;
void remove_connection(int socket);
void update_maxfd(void);
#ifdef DEBUG
void dumpConnections(void);
#else
inline void dumpConnections(void) {}
#endif
#else /* SUPPORT_DTLS_OVER_SCTP */
/* This is the code that gets compiled if SUPPORT_DTLS_OVER_SCTP is not set */
public:
IpfixReceiverDtlsSctpIpV4(int port, const std::string ipAddr = "",
const std::string &certificateChainFile = "", const std::string &privateKeyFile = "",
const std::string &caFile = "", const std::string &caPath = "",
const std::set<string> &peerFqdns = std::set<string>() ) {
THROWEXCEPTION("DTLS over SCTP not supported!");
}
virtual ~IpfixReceiverDtlsSctpIpV4() {}
virtual void run() {}
virtual std::string getStatisticsXML(double interval) { return ""; }
#endif /*SUPPORT_DTLS_OVER_SCTP*/
};
#endif

View File

@ -0,0 +1,484 @@
/* vim: set sts=4 sw=4 cindent nowrap: This modeline was added by Daniel Mentz */
/*
* IPFIX Concentrator Module Library
* Copyright (C) 2004 Christoph Sommer <http://www.deltadevelopment.de/users/christoph/ipfix/>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifdef SUPPORT_DTLS
#include "IpfixReceiverDtlsUdpIpV4.hpp"
#include "IpfixPacketProcessor.hpp"
#include "IpfixParser.hpp"
#include "common/msg.h"
#include "common/openssl/OpenSSL.h"
#include <stdexcept>
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sstream>
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
#ifndef HEADER_DH_H
#include <openssl/dh.h>
#endif
using namespace std;
/**
* Does DTLS over UDP/IPv4 specific initialization.
* @param port Port to listen on
* @param ipAddr IP address to bind to our socket, if equals "", no specific IP address will be bound.
*/
IpfixReceiverDtlsUdpIpV4::IpfixReceiverDtlsUdpIpV4(int port, const std::string ipAddr,
const std::string &certificateChainFile, const std::string &privateKeyFile,
const std::string &caFile, const std::string &caPath,
const std::set<string> &peerFqdnsParam)
: IpfixReceiver(port),listen_socket(-1),
ssl_ctx(certificateChainFile,privateKeyFile,caFile,caPath, ! peerFqdnsParam.empty()),
peerFqdns(peerFqdnsParam),
statReceivedPackets(0),
connections(my_CompareSourceID)
{
struct sockaddr_in serverAddress;
try {
listen_socket = socket(AF_INET, SOCK_DGRAM, 0);
if(listen_socket < 0) {
/* FIXME: should we use strerror_r? */
msg(MSG_FATAL, "Could not create socket: %s", strerror(errno));
THROWEXCEPTION("Cannot create IpfixReceiverDtlsUdpIpV4, socket creation failed");
}
// if ipAddr set: Bind a specific IP address to our socket.
// else: use wildcard address
if(ipAddr.empty())
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
else
if ( ! inet_pton(AF_INET,ipAddr.c_str(),&serverAddress.sin_addr) ) {
THROWEXCEPTION("IP address invalid.");
}
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(port);
if(bind(listen_socket, (struct sockaddr*)&serverAddress,
sizeof(struct sockaddr_in)) < 0) {
msg(MSG_FATAL, "Could not bind socket: %s", strerror(errno));
THROWEXCEPTION("Cannot create IpfixReceiverDtlsUdpIpV4 %s:%d",ipAddr.c_str(), port );
}
/* TODO: Find out what this is? */
SensorManager::getInstance().addSensor(this, "IpfixReceiverDtlsUdpIpV4", 0);
msg(MSG_INFO, "DTLS over UDP Receiver listening on %s:%d, FD=%d", (ipAddr == "")?std::string("ALL").c_str() : ipAddr.c_str(),
port,
listen_socket);
} catch(...) {
if (listen_socket>=0) {
close(listen_socket);
listen_socket = -1;
}
throw; // rethrow
}
}
/**
* Does UDP/IPv4 specific cleanup
*/
IpfixReceiverDtlsUdpIpV4::~IpfixReceiverDtlsUdpIpV4() {
close(listen_socket);
}
#ifdef DEBUG
void IpfixReceiverDtlsUdpIpV4::dumpConnections() {
struct in_addr addr;
char ipaddr[INET_ADDRSTRLEN];
DPRINTF("Dumping DTLS connections:");
if (connections.empty()) {
DPRINTF(" (none)");
return;
}
connections_map::const_iterator it = connections.begin();
for(;it!=connections.end();it++) {
const IpfixRecord::SourceID &sourceID = it->first;
memcpy(&addr.s_addr,sourceID.exporterAddress.ip, sourceID.exporterAddress.len);
inet_ntop(AF_INET,&addr,ipaddr,sizeof(ipaddr));
DPRINTF(" %s",it->second->inspect().c_str());
}
}
#endif
const char *IpfixReceiverDtlsUdpIpV4::DtlsConnection::states[] = {
"ACCEPTING","CONNECTED","SHUTDOWN"
};
int verify_peer_cb_udp(void *context, const char *dnsname) {
IpfixReceiverDtlsUdpIpV4 *receiver =
static_cast<IpfixReceiverDtlsUdpIpV4 *>(context);
string strdnsname(dnsname);
transform(strdnsname.begin(),strdnsname.end(),strdnsname.begin(),
::tolower);
if (receiver->peerFqdns.find(strdnsname)!=receiver->peerFqdns.end())
return 1;
else
return 0;
}
int IpfixReceiverDtlsUdpIpV4::DtlsConnection::verify_peer() {
return verify_ssl_peer(ssl,&verify_peer_cb_udp,&parent);
}
/**
* UDP specific listener function. This function is called by @c listenerThread()
*/
void IpfixReceiverDtlsUdpIpV4::run() {
struct sockaddr_in clientAddress;
socklen_t clientAddressLen;
clientAddressLen = sizeof(struct sockaddr_in);
fd_set fd_array; //all active filedescriptors
fd_set readfds; //parameter for for pselect
int ret;
struct timespec timeOut;
int timeout_count = 0;
FD_ZERO(&fd_array);
FD_SET(listen_socket, &fd_array);
/* set a 400ms time-out on the pselect */
timeOut.tv_sec = 0L;
timeOut.tv_nsec = 400000000L;
while(!exitFlag) {
readfds = fd_array; // because select() changes readfds
ret = pselect(listen_socket + 1, &readfds, NULL, NULL, &timeOut, NULL);
if (ret == 0) {
/* Timeout */
if (++timeout_count == 10) {
idle_processing();
timeout_count = 0;
}
continue;
}
if ((ret == -1) && (errno == EINTR)) {
/* There was a signal... ignore */
continue;
}
if (ret < 0) {
msg(MSG_ERROR ,"select() returned with an error");
THROWEXCEPTION("IpfixReceiverDtlsUdpIpV4: terminating listener thread");
break;
}
boost::shared_array<uint8_t> secured_data(new uint8_t[MAX_MSG_LEN]);
boost::shared_array<uint8_t> data(new uint8_t[MAX_MSG_LEN]);
boost::shared_ptr<IpfixRecord::SourceID> sourceID(new IpfixRecord::SourceID);
ret = recvfrom(listen_socket, secured_data.get(), MAX_MSG_LEN,
0, (struct sockaddr*)&clientAddress, &clientAddressLen);
if (ret < 0) {
msg(MSG_FATAL, "recvfrom returned without data, terminating listener thread");
break;
}
if ( ! isHostAuthorized(&clientAddress.sin_addr, sizeof(clientAddress.sin_addr))) {
msg(MSG_FATAL, "packet from unauthorized host %s discarded", inet_ntoa(clientAddress.sin_addr));
continue;
}
#ifdef DEBUG
char ipaddr[INET_ADDRSTRLEN];
inet_ntop(AF_INET,&clientAddress.sin_addr,ipaddr,sizeof(ipaddr));
DPRINTF("Received packet of size %d from %s:%d",ret,ipaddr,ntohs(clientAddress.sin_port));
#endif
/* Set up sourceID */
memcpy(sourceID->exporterAddress.ip, &clientAddress.sin_addr.s_addr, 4);
sourceID->exporterAddress.len = 4;
sourceID->exporterPort = ntohs(clientAddress.sin_port);
sourceID->protocol = IPFIX_protocolIdentifier_UDP;
sourceID->receiverPort = receiverPort;
sourceID->fileDescriptor = listen_socket;
/* Search for an existing connection with same source IP and port */
connections_map::iterator it = connections.find(*sourceID);
DtlsConnectionPtr conn;
if (it == connections.end()) {
/* create a new connection if we did not find any. */
DPRINTF("New connection");
if (connections.size() >= DTLS_MAX_CONCURRENT_CONNECTIONS) {
msg(MSG_ERROR,"Maximum number (%d) of concurrent "
"connections reached. Ignoring new connection "
"attempt.",DTLS_MAX_CONCURRENT_CONNECTIONS);
continue;
}
conn = DtlsConnectionPtr( new DtlsConnection(*this,&clientAddress));
it = connections.insert(make_pair(*sourceID,conn)).first;
} else {
/* Use existing connection */
DPRINTF("Found existing connection.");
conn = it->second;
}
conn->consumeDatagram(sourceID, secured_data,ret);
dumpConnections();
}
msg(MSG_DEBUG, "IpfixReceiverDtlsUdpIpV4: Exiting");
}
/**
* statistics function called by StatisticsManager
*/
std::string IpfixReceiverDtlsUdpIpV4::getStatisticsXML(double interval)
{
ostringstream oss;
oss << "<receivedPackets>" << statReceivedPackets << "</receivedPackets>" << endl;
return oss.str();
}
IpfixReceiverDtlsUdpIpV4::DtlsConnection::DtlsConnection(IpfixReceiverDtlsUdpIpV4 &_parent,struct sockaddr_in *pclientAddress) :
parent(_parent), state(ACCEPTING), last_used(time(NULL)) {
ssl = parent.ssl_ctx.SSL_new();
memcpy(&clientAddress, pclientAddress, sizeof clientAddress);
BIO *sbio, *rbio;
/* create output abstraction for SSL object */
sbio = BIO_new_dgram(parent.listen_socket,BIO_NOCLOSE);
/* create a dummy BIO that always returns EOF */
rbio = BIO_new(BIO_s_mem());
/* -1 means EOF */
BIO_set_mem_eof_return(rbio,-1);
SSL_set_bio(ssl,rbio,sbio);
SSL_set_accept_state(ssl);
BIO_ctrl(ssl->wbio,BIO_CTRL_DGRAM_SET_PEER,0,&clientAddress);
}
IpfixReceiverDtlsUdpIpV4::DtlsConnection::~DtlsConnection() {
if (ssl) parent.ssl_ctx.SSL_free(ssl);
}
#ifdef DEBUG
std::string IpfixReceiverDtlsUdpIpV4::DtlsConnection::inspect(bool includeState) {
std::ostringstream o;
char ipaddr[INET_ADDRSTRLEN];
inet_ntop(AF_INET,&clientAddress.sin_addr,ipaddr,sizeof(ipaddr));
o << ipaddr << ":" << ntohs(clientAddress.sin_port);
if (includeState)
o << " state:" << states[state];
return o.str();
}
#endif
/* Accepts a DTLS connection
* Returns values:
* 1 Successfully connected (don't call accept() again)
* 0 Not yet connected (call accept() again as soon as new data is available)
* -1 Failure. Remove this connection
*/
int IpfixReceiverDtlsUdpIpV4::DtlsConnection::accept() {
int ret, error;
char buf[512];
ret = SSL_accept(ssl);
if (SSL_get_shared_ciphers(ssl,buf,sizeof buf) != NULL)
DPRINTF("Shared ciphers:%s",buf);
if (ret==1) {
state = CONNECTED;
DPRINTF("SSL_accept() succeeded.");
const char *str=SSL_CIPHER_get_name(SSL_get_current_cipher(ssl));
DPRINTF("CIPHER is %s",(str != NULL)?str:"(NONE)");
if (parent.ssl_ctx.get_verify_peers()) {
if (verify_peer()) {
DPRINTF("Peer authentication successful.");
} else {
msg(MSG_ERROR,"Peer authentication failed. Shutting down connection.");
shutdown();
return -1;
}
}
return 1;
}
error = SSL_get_error(ssl,ret);
DPRINTF("SSL_accept() returned: %d, error: %d, strerror: %s",ret,error,strerror(errno));
if (ret==-1 && error == SSL_ERROR_WANT_READ) {
DPRINTF("SSL_accept() returned SSL_ERROR_WANT_READ");
return 0;
}
msg(MSG_ERROR,"SSL_accept() failed.");
long verify_result = SSL_get_verify_result(ssl);
if(SSL_get_verify_result(ssl)!=X509_V_OK) {
msg(MSG_ERROR,"Last verification error: %s", X509_verify_cert_error_string(verify_result));
}
state = SHUTDOWN;
msg_openssl_errors();
return -1;
}
void IpfixReceiverDtlsUdpIpV4::DtlsConnection::shutdown() {
int ret, error;
ret = SSL_shutdown(ssl);
if (ret == 0) {
DPRINTF("Calling SSL_shutdown a second time.");
ret = SSL_shutdown(ssl);
}
error = SSL_get_error(ssl,ret);
DPRINTF("SSL_shutdown() returned: %d, error: %d, strerror: %s",ret,error,strerror(errno));
state = SHUTDOWN;
}
/* Return values:
* 1 Success
* 0 Failure. Remove this connection
*/
int IpfixReceiverDtlsUdpIpV4::DtlsConnection::consumeDatagram(
boost::shared_ptr<IpfixRecord::SourceID> &sourceID,
boost::shared_array<uint8_t> secured_data, size_t len) {
int ret, error;
last_used = time(NULL);
if (state == SHUTDOWN) {
DPRINTF("state == SHUTDOWN. Ignoring datagram");
return 1;
}
#ifdef DEBUG
if ( ! BIO_eof(ssl->rbio)) {
msg(MSG_ERROR,"EOF *not* reached on BIO. This should not happen.");
}
#endif
BIO_free(ssl->rbio);
ssl->rbio = BIO_new_mem_buf(secured_data.get(),len);
BIO_set_mem_eof_return(ssl->rbio,-1);
if (state == ACCEPTING) {
ret = accept();
if (ret == 0) return 1;
if (ret == -1) return 0;
#ifdef DEBUG
if ( ! BIO_eof(ssl->rbio)) {
msg(MSG_ERROR,"EOF *not* reached on BIO. This should not happen.");
}
#endif
if (BIO_eof(ssl->rbio)) return 1; /* This should always be the case */
}
boost::shared_array<uint8_t> data(new uint8_t[MAX_MSG_LEN]);
ret = SSL_read(ssl,data.get(),MAX_MSG_LEN);
error = SSL_get_error(ssl,ret);
DPRINTF("SSL_read() returned: %d, error: %d, strerror: %s",ret,error,strerror(errno));
if (ret<0) {
if (error == SSL_ERROR_WANT_READ)
return 1;
msg(MSG_ERROR,"SSL_read() failed. SSL_get_error() returned: %d",error);
msg_openssl_errors();
shutdown();
return 0;
} else if (ret==0) {
if (error == SSL_ERROR_ZERO_RETURN) {
// remote side closed connection
DPRINTF("remote side closed connection.");
} else {
msg(MSG_ERROR,"SSL_read() returned 0. SSL_get_error() returned: %d",error);
msg_openssl_errors();
}
shutdown();
return 0;
} else {
DPRINTF("SSL_read() returned %d bytes.",ret);
}
parent.statReceivedPackets++;
parent.mutex.lock();
for (std::list<IpfixPacketProcessor*>::iterator i = parent.packetProcessors.begin();
i != parent.packetProcessors.end(); ++i) {
(*i)->processPacket(data, ret, sourceID);
}
parent.mutex.unlock();
return 1;
}
void IpfixReceiverDtlsUdpIpV4::idle_processing() {
/* Iterate over all connections and remove those that appear
* to be unused. */
connections_map::iterator tmp;
connections_map::iterator it = connections.begin();
bool changed = false;
while(it!=connections.end()) {
tmp = it++;
if (tmp->second->isInactive()) {
DPRINTF("Removing connection %s",tmp->second->inspect(false).c_str());
connections.erase(tmp);
changed = true;
}
}
if (changed) {
dumpConnections();
}
}
/* Checks whether the connection timed out.
* Watch out: This method has side effects. If the connection did
* timeout it returns true and expects the caller to remove the connection
* from the connection map.
* Return values:
* false: Connection is still active. Keep it.
* true: Connection is idle and has been shut down. Remove it! */
bool IpfixReceiverDtlsUdpIpV4::DtlsConnection::isInactive() {
time_t diff = time(NULL) - last_used;
switch (state) {
case ACCEPTING:
if (diff > DTLS_ACCEPT_TIMEOUT) {
DPRINTF("accept timed out on %s",inspect(false).c_str());
shutdown();
return true;
}
break;
case CONNECTED:
if (diff > DTLS_IDLE_TIMEOUT) {
DPRINTF("idle timeout on %s",inspect(false).c_str());
shutdown();
return true;
}
break;
case SHUTDOWN:
if (diff > DTLS_SHUTDOWN_TIMEOUT) {
DPRINTF("shutdown timeout on %s",inspect(false).c_str());
return true;
}
break;
}
return false;
}
#endif /*SUPPORT_DTLS*/

View File

@ -0,0 +1,142 @@
/* vim: set sts=4 sw=4 cindent nowrap: This modeline was added by Daniel Mentz */
/*
* IPFIX Concentrator Module Library
* Copyright (C) 2004 Christoph Sommer <http://www.deltadevelopment.de/users/christoph/ipfix/>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef _IPFIX_RECEIVER_DTLSUDPIPV4_H_
#define _IPFIX_RECEIVER_DTLSUDPIPV4_H_
#include <pthread.h>
#include <stdint.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <list>
#include <map>
#include <set>
#include "IpfixReceiver.hpp"
#include "IpfixPacketProcessor.hpp"
#ifdef SUPPORT_DTLS
#include "common/openssl/SSLCTXWrapper.hpp"
#include <openssl/ssl.h>
#ifndef HEADER_DH_H
#include <openssl/dh.h>
#endif
/* Maximum amount of time in seconds a connection may remain in ACCEPT state. */
#define DTLS_ACCEPT_TIMEOUT 30
/* Idle timeout in seconds. If no datagrams have been received on a
* connection for that amount of time the connection will be removed. */
#define DTLS_IDLE_TIMEOUT (12 * 60 * 60)
/* Time in seconds a connection stays in state SHUTDOWN before it gets
* removed. */
#define DTLS_SHUTDOWN_TIMEOUT 10
/* Maximum number of concurrent connections */
#define DTLS_MAX_CONCURRENT_CONNECTIONS 50
#endif /* SUPPORT_DTLS */
class IpfixReceiverDtlsUdpIpV4 : public IpfixReceiver, Sensor {
#ifdef SUPPORT_DTLS
public:
IpfixReceiverDtlsUdpIpV4(int port, const std::string ipAddr = "",
const std::string &certificateChainFile = "", const std::string &privateKeyFile = "",
const std::string &caFile = "", const std::string &caPath = "",
const std::set<string> &peerFqdns = std::set<string>() );
virtual ~IpfixReceiverDtlsUdpIpV4();
virtual void run();
virtual std::string getStatisticsXML(double interval);
private:
int listen_socket;
SSL_CTX_wrapper ssl_ctx;
const std::set<string> peerFqdns;
friend int verify_peer_cb_udp(void *context, const char *dnsname);
uint32_t statReceivedPackets; /**< number of received packets */
class DtlsConnection {
public:
DtlsConnection(IpfixReceiverDtlsUdpIpV4 &parent,struct sockaddr_in *clientAddress);
~DtlsConnection();
int consumeDatagram(boost::shared_ptr<IpfixRecord::SourceID> &sourceID, boost::shared_array<uint8_t> secured_data, size_t len);
std::string inspect(bool includeState = true);
bool isInactive();
private:
struct sockaddr_in clientAddress;
IpfixReceiverDtlsUdpIpV4 &parent;
SSL* ssl;
enum state_t { ACCEPTING, CONNECTED, SHUTDOWN };
static const char *states[];
state_t state;
time_t last_used;
int accept();
void shutdown();
int verify_peer();
};
typedef boost::shared_ptr<DtlsConnection> DtlsConnectionPtr;
typedef bool (*CompareSourceID)(const IpfixRecord::SourceID&, const IpfixRecord::SourceID&);
// compare based on source address and source port
static bool my_CompareSourceID(const IpfixRecord::SourceID& lhs, const IpfixRecord::SourceID& rhs) {
int result;
if (lhs.exporterAddress.len < rhs.exporterAddress.len) return true;
if (lhs.exporterAddress.len > rhs.exporterAddress.len) return false;
result = memcmp(lhs.exporterAddress.ip, rhs.exporterAddress.ip, lhs.exporterAddress.len);
if (result < 0) return true;
if (result > 0) return false;
if (lhs.exporterPort < rhs.exporterPort) return true;
return false;
}
typedef std::map<IpfixRecord::SourceID,DtlsConnectionPtr,CompareSourceID> connections_map;
connections_map connections;
void idle_processing();
#ifdef DEBUG
void dumpConnections(void);
#else
inline void dumpConnections(void) {}
#endif
#else /* SUPPORT_DTLS */
/* This is the code that gets compiled if SUPPORT_DTLS is not set */
public:
IpfixReceiverDtlsUdpIpV4(int port, const std::string ipAddr = "",
const std::string &certificateChainFile = "", const std::string &privateKeyFile = "",
const std::string &caFile = "", const std::string &caPath = "",
const std::set<string> &peerFqdns = std::set<string>() ) {
THROWEXCEPTION("DTLS over UDP not supported!");
}
virtual ~IpfixReceiverDtlsUdpIpV4() {}
virtual void run() {}
virtual std::string getStatisticsXML(double interval) { return ""; }
#endif /* SUPPORT_DTLS */
};
#endif

View File

@ -66,7 +66,7 @@ IpfixReceiverSctpIpV4::IpfixReceiverSctpIpV4(int port, std::string ipAddr) {
perror("Could not bind socket");
THROWEXCEPTION("Cannot create IpfixReceiverSctpIpV4 %s:%d",ipAddr.c_str(), port );
}
if(listen(listen_socket, SCTP_MAX_CONNECTIONS) < 0 ) {
if(listen(listen_socket, SCTP_MAX_BACKLOG) < 0 ) {
msg(MSG_ERROR ,"Could not listen on SCTP socket %i", listen_socket);
THROWEXCEPTION("Cannot create IpfixReceiverSctpIpV4");
}

View File

@ -30,11 +30,15 @@
#include "IpfixReceiver.hpp"
#include "IpfixPacketProcessor.hpp"
//Maximum number of simultanious connections
#define SCTP_MAX_CONNECTIONS 5
// Quote from man page: "maximum length to which the queue of pending connections
// for sockfd may grow. If a connection request arrives when the queue is
// full, the client may receive an error with an indication of ECONNREFUSED
// or, if the underlying protocol supports retransmission, the request may
// be ignored so that a later reattempt at connection succeeds."
#define SCTP_MAX_BACKLOG 5
class IpfixReceiverSctpIpV4 : public IpfixReceiver {
class IpfixReceiverSctpIpV4 : public IpfixReceiver, Sensor {
#ifdef SUPPORT_SCTP
public:
IpfixReceiverSctpIpV4(int port, std::string ipAddr = "");

View File

@ -52,7 +52,9 @@ IpfixReceiverUdpIpV4::IpfixReceiverUdpIpV4(int port, std::string ipAddr)
listen_socket = socket(AF_INET, SOCK_DGRAM, 0);
if(listen_socket < 0) {
/* ASK: error should be written to log file */
perror("Could not create socket");
/* ASK: Why not throw? printf format */
THROWEXCEPTION("Cannot create IpfixReceiverUdpIpV4, socket creation failed");
}
@ -61,6 +63,7 @@ IpfixReceiverUdpIpV4::IpfixReceiverUdpIpV4(int port, std::string ipAddr)
if(ipAddr == "")
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
else
/* ASK: check return value. inet_addr() is obsolete. inet_aton should be used. */
serverAddress.sin_addr.s_addr = inet_addr(ipAddr.c_str());
serverAddress.sin_family = AF_INET;

View File

@ -38,14 +38,21 @@ using namespace std;
#define SENDER_TEMPLATE_ID_HI 65535
/**
* Creates a new IPFIX Exporter. Do not forget to call @c startIpfixSender() to begin sending
* Creates a new IPFIX Exporter. Do not forget to call @c startIpfixSender() to
* begin sending
* @param sourceID Source ID this exporter will report
* @param ip destination collector's address
* @param port destination collector's port
* @return handle to use when calling @c destroyIpfixSender()
*/
IpfixSender::IpfixSender(uint32_t observationDomainId, uint32_t maxRecordRate, uint32_t sctpDataLifetime, uint32_t sctpReconnectInterval,
uint32_t templateRefreshInterval, uint32_t templateRefreshRate)
IpfixSender::IpfixSender(uint32_t observationDomainId, uint32_t maxRecordRate,
uint32_t sctpDataLifetime, uint32_t sctpReconnectInterval,
uint32_t templateRefreshInterval,
const std::string &certificateChainFile,
const std::string &privateKeyFile,
const std::string &caFile,
const std::string &caPath)
: statSentDataRecords(0),
statSentPackets(0),
statPacketsInFlows(0),
@ -55,6 +62,11 @@ IpfixSender::IpfixSender(uint32_t observationDomainId, uint32_t maxRecordRate, u
currentTemplateId(0),
maxRecordRate(maxRecordRate)
{
const char *certificate_chain_file = NULL;
const char *private_key_file = NULL;
const char *ca_file = NULL;
const char *ca_path = NULL;
ipfix_exporter** exporterP = &this->ipfixExporter;
nextTimeout.tv_sec = 0;
@ -71,6 +83,21 @@ IpfixSender::IpfixSender(uint32_t observationDomainId, uint32_t maxRecordRate, u
ipfix_set_sctp_reconnect_timer(ipfixExporter, sctpReconnectInterval);
ipfix_set_template_transmission_timer(ipfixExporter, templateRefreshInterval);
if ( ! certificateChainFile.empty())
certificate_chain_file = certificateChainFile.c_str();
if ( ! privateKeyFile.empty())
private_key_file = privateKeyFile.c_str();
/* Private key will be searched for in the certificate chain file if
* no private key file is set */
if (certificate_chain_file || private_key_file)
ipfix_set_dtls_certificate(ipfixExporter,
certificate_chain_file, private_key_file);
if ( ! caFile.empty() ) ca_file = caFile.c_str();
if ( ! caPath.empty() ) ca_path = caPath.c_str();
if (ca_file || ca_path)
ipfix_set_ca_locations(ipfixExporter, ca_file, ca_path);
msg(MSG_DEBUG, "IpfixSender: running");
return;
@ -128,42 +155,60 @@ IpfixSender::~IpfixSender()
* @param ips handle to the Exporter
* @param ip string of the IP
* @param port port number
* @param aux_config additional configuration details required for UDP,
* DTLS_OVER_UDP and DTLS_OVER_SCTP. See ipfixlolib documentation for more
* information.
* FIXME: support for other than UDP
*/
void IpfixSender::addCollector(const char *ip, uint16_t port, uint16_t proto)
void IpfixSender::addCollector(const char *ip, uint16_t port,
ipfix_transport_protocol proto, void *aux_config)
{
ipfix_exporter *ex = (ipfix_exporter *)ipfixExporter;
switch(proto) {
case 17:
msg(MSG_INFO, "IpfixSender: adding UDP://%s:%u to exporter", ip, port);
if(ipfix_add_collector(ipfixExporter, ip, port, UDP) != 0) {
msg(MSG_FATAL, "IpfixSender: ipfix_add_collector of %s:%u failed", ip, port);
}
case UDP:
msg(MSG_INFO, "IpfixSender: adding UDP://%s:%d to exporter",
ip, port);
break;
case 132:
msg(MSG_INFO, "IpfixSender: adding SCTP://%s:%u to exporter", ip, port);
if(ipfix_add_collector(ipfixExporter, ip, port, SCTP) != 0) {
msg(MSG_FATAL, "IpfixSender: ipfix_add_collector of %s:%u failed", ip, port);
}
case SCTP:
msg(MSG_INFO, "IpfixSender: adding SCTP://%s:%d to exporter",
ip, port);
break;
#ifdef IPFIXLOLIB_RAWDIR_SUPPORT
case 0:
msg(MSG_INFO, "IpfixSender: adding RAWDIR://%s to exporter", ip);
if(ipfix_add_collector(ipfixExporter, ip, port, RAWDIR) != 0) {
msg(MSG_FATAL, "IpfixSender: ipfix_add_collector of %s:%u failed", ip, port);
}
case RAWDIR:
msg(MSG_INFO, "IpfixSender: adding RAWDIR://%s to exporter",
ip);
break;
#endif
case 6:
msg(MSG_INFO, "IpfixSender: adding TCP://%s:%u to exporter", ip, port);
if(ipfix_add_collector(ipfixExporter, ip, port, TCP) != 0) {
msg(MSG_FATAL, "IpfixSender: ipfix_add_collector of %s:%u failed", ip, port);
}
#ifdef SUPPORT_DTLS
case DTLS_OVER_UDP:
msg(MSG_INFO,
"IpfixSender: adding DTLS over UDP://%s:%d to exporter",
ip, port);
break;
#endif
#ifdef SUPPORT_DTLS_OVER_SCTP
case DTLS_OVER_SCTP:
msg(MSG_INFO,
"IpfixSender: adding DTLS over SCTP://%s:%d to exporter",
ip, port);
break;
#endif
case TCP:
msg(MSG_INFO, "IpfixSender: adding TCP://%s:%d to exporter",
ip, port);
default:
THROWEXCEPTION("IpfixSender: Invalid protocol (%u) given!", proto);
THROWEXCEPTION("invalid protocol (%d) given!", proto);
break;
}
}
if(ipfix_add_collector(ex, ip, port, proto, aux_config) != 0) {
msg(MSG_FATAL,
"IpfixSender: ipfix_add_collector of %s:%d failed",
ip, port);
return;
}
}
/**
* Get a small, unused Template Id
@ -280,8 +325,8 @@ void IpfixSender::onTemplate(IpfixTemplateRecord* record)
}
}
if (0 != ipfix_start_datatemplate_set(ipfixExporter, my_template_id, my_preceding, dataTemplateInfo->fieldCount + splitFields, dataTemplateInfo->dataCount + splitFixedfields)) {
THROWEXCEPTION("IpfixSender: ipfix_start_datatemplate_set failed");
if (0 != ipfix_start_datatemplate(ipfixExporter, my_template_id, my_preceding, dataTemplateInfo->fieldCount + splitFields, dataTemplateInfo->dataCount + splitFixedfields)) {
THROWEXCEPTION("IpfixSender: ipfix_start_datatemplate failed");
}
for (i = 0; i < dataTemplateInfo->fieldCount; i++) {
@ -350,8 +395,8 @@ void IpfixSender::onTemplate(IpfixTemplateRecord* record)
}
free(data);
if (0 != ipfix_end_template_set(ipfixExporter, my_template_id)) {
THROWEXCEPTION("IpfixSender: ipfix_end_template_set failed");
if (0 != ipfix_end_template(ipfixExporter, my_template_id)) {
THROWEXCEPTION("IpfixSender: ipfix_end_template failed");
}
msg(MSG_INFO, "IpfixSender: created template with ID %u", my_template_id);
@ -413,8 +458,8 @@ void IpfixSender::onTemplateDestruction(IpfixTemplateDestructionRecord* record)
templateIdToUniqueId.erase(my_template_id);
/* Remove template from ipfixlolib */
if (0 != ipfix_remove_template_set(ipfixExporter, my_template_id)) {
msg(MSG_FATAL, "IpfixSender: ipfix_remove_template_set failed");
if (0 != ipfix_remove_template(ipfixExporter, my_template_id)) {
msg(MSG_FATAL, "IpfixSender: ipfix_remove_template failed");
}
else
{
@ -443,69 +488,67 @@ void IpfixSender::onTemplateDestruction(IpfixTemplateDestructionRecord* record)
* @param templateId of the new Data Set
* @return returns -1 on error, 0 otherwise
*/
void IpfixSender::startDataSet(TemplateInfo::TemplateId templateId)
void IpfixSender::setTemplateId(TemplateInfo::TemplateId templateId, uint16_t dataLength)
{
ipfix_exporter* exporter = (ipfix_exporter*)ipfixExporter;
uint16_t my_n_template_id = htons(templateId);
/* check if we can use the current Data Set */
//TODO: make maximum number of records per Data Set variable
if((noCachedRecords < 10) && (templateId == currentTemplateId))
if (currentTemplateId == 0)
; /* Do nothing */
else if(templateId != currentTemplateId) {
endDataSet();
if (remainingSpace < dataLength + IPFIX_OVERHEAD_PER_SET) {
send();
}
} else if (remainingSpace < dataLength) {
endDataSet();
send();
} else {
return;
if(noCachedRecords > 0)
endAndSendDataSet();
if (ipfix_start_data_set(ipfixExporter, my_n_template_id) != 0 ) {
THROWEXCEPTION("IpfixSender: ipfix_start_data_set failed!");
}
if (ipfix_start_data_set(exporter, my_n_template_id) != 0 ) {
THROWEXCEPTION("ipfix_start_data_set failed!");
}
remainingSpace = ipfix_get_remaining_space(exporter);
currentTemplateId = templateId;
}
/**
* Terminates and sends current Data Set if available.
* Terminates current Data Set.
* @return returns -1 on error, 0 otherwise
*/
void IpfixSender::endAndSendDataSet()
void IpfixSender::endDataSet()
{
if(noCachedRecords > 0) {
if (ipfix_end_data_set(ipfixExporter, noCachedRecords) != 0) {
THROWEXCEPTION("IpfixSender: ipfix_end_data_set failed");
}
// determine if we need to wait (we don't want to exceed the defined packet rate per second)
// check in 100ms steps if maximum packet rate is reached - if yes, wait until the 100ms step
// is over
struct timeval tv;
gettimeofday(&tv, 0);
if ((tv.tv_sec==curTimeStep.tv_sec) && (tv.tv_usec/100000==curTimeStep.tv_usec/100000)) {
if (recordsSentStep>maxRecordRate/10) {
// wait until current timestep is over
usleep(100000-(tv.tv_usec%100000));
}
} else {
curTimeStep = tv;
recordsSentStep = 0;
}
if (ipfix_send(ipfixExporter) != 0) {
THROWEXCEPTION("IpfixSender: ipfix_send failed");
}
statSentPackets++;
removeRecordReferences();
currentTemplateId = 0;
} else { // only send Templates
DPRINTF("IpfixSender::endAndSendDataSet: Send Templates only");
if (ipfix_send(ipfixExporter) != 0) {
THROWEXCEPTION("IpfixSender: ipfix_send failed");
}
if (ipfix_end_data_set(ipfixExporter, noRecordsInCurrentSet) != 0) {
THROWEXCEPTION("ipfix_end_data_set failed");
}
noRecordsInCurrentSet = 0;
currentTemplateId = 0;
}
void IpfixSender::send() {
// determine if we need to wait (we don't want to exceed the defined packet rate per second)
// check in 100ms steps if maximum packet rate is reached - if yes, wait until the 100ms step
// is over
struct timeval tv;
gettimeofday(&tv, 0);
if ((tv.tv_sec==curTimeStep.tv_sec) && (tv.tv_usec/100000==curTimeStep.tv_usec/100000)) {
if (recordsSentStep>maxRecordRate/10) {
// wait until current timestep is over
usleep(100000-(tv.tv_usec%100000));
}
} else {
curTimeStep = tv;
recordsSentStep = 0;
}
if (ipfix_send(ipfixExporter) != 0) {
THROWEXCEPTION("sndIpfix: ipfix_send failed");
}
removeRecordReferences();
}
/**
* removes references to flows inside buffer recordsToRelease
@ -561,7 +604,7 @@ void IpfixSender::onDataRecord(IpfixDataRecord* record)
return;
}
startDataSet(my_template_id);
setTemplateId(my_template_id, record->dataLength);
int i;
for (i = 0; i < dataTemplateInfo->fieldCount; i++) {
@ -589,18 +632,18 @@ void IpfixSender::onDataRecord(IpfixDataRecord* record)
ipfix_put_data_field(ipfixExporter, data + fi->offset, fi->type.length);
}
}
remainingSpace -= record->dataLength;
statSentDataRecords++;
recordsSentStep++;
recordsToRelease.push(record);
noCachedRecords++;
noRecordsInCurrentSet++;
registerTimeout();
// release the message lock
ipfixMessageLock.unlock();
sendRecords();
}
/**
@ -621,8 +664,8 @@ void IpfixSender::onReconfiguration2()
// Destroy all templates (they will be resent after reconfiguration if necessary)
for(map<TemplateInfo::TemplateId, uint16_t>::iterator iter = templateIdToUniqueId.begin(); iter != templateIdToUniqueId.end(); iter++) {
/* Remove template from ipfixlolib */
if (0 != ipfix_remove_template_set(ipfixExporter, iter->first)) {
msg(MSG_FATAL, "IpfixSender: ipfix_remove_template_set failed");
if (0 != ipfix_remove_template(ipfixExporter, iter->first)) {
msg(MSG_FATAL, "IpfixSender: ipfix_remove_template failed");
}
else
{
@ -658,40 +701,39 @@ void IpfixSender::sendRecords(SendPolicy policy)
return;
}
// send if packet is full or if sending is forced
// TODO: extend ipfixlolib so that as many records as possible may be stored
// in one network packet
if ((noCachedRecords >= 10) || (policy != IfFull)) {
// send packet now
endAndSendDataSet();
} else { // don't send now but after timeout
// set next timeout
addToCurTime(&nextTimeout, recordCacheTimeout);
registerTimeout();
}
// We cancel the timeout because we're about to send
// out all records.
timeoutRegistered = false;
// send packet
if (currentTemplateId) endDataSet();
send();
statSentPackets++;
// get the message lock
ipfixMessageLock.unlock();
}
/**
* gets called regularly to send data over the network
*/
void IpfixSender::onTimeout(void* dataPtr)
{
timeoutRegistered = false;
void IpfixSender::onSendRecordsTimeout(void) {
if (!timeoutRegistered) return;
timeval tv;
gettimeofday(&tv, 0);
// check if this timeout corresponds to nextTimeout
if (nextTimeout.tv_sec>tv.tv_sec || (nextTimeout.tv_sec==tv.tv_sec && nextTimeout.tv_nsec>tv.tv_usec*1000))
// next timeout is in the future, reregister it
registerTimeout();
else
// timeout corresponds to nextTimeout, so force sending the message
sendRecords(IfNotEmpty);
if (nextTimeout.tv_sec<tv.tv_sec || (nextTimeout.tv_sec==tv.tv_sec && nextTimeout.tv_nsec<tv.tv_usec*1000)) {
sendRecords(Always);
}
}
void IpfixSender::onBeatTimeout(void) {
ipfix_beat(ipfixExporter);
}
void IpfixSender::onTimeout(void* dataPtr)
{
onSendRecordsTimeout();
onBeatTimeout();
registerBeatTimeout();
}
/**
@ -700,15 +742,23 @@ void IpfixSender::onTimeout(void* dataPtr)
*/
void IpfixSender::registerTimeout()
{
// check if there is already a timeout
if (timeoutRegistered) return;
if(timer){
timer->addTimeout(this, nextTimeout, NULL);
timeoutRegistered = true;
}
else {
msg(MSG_DEBUG, "timer == NULL, this = %p", this);
}
addToCurTime(&nextTimeout, recordCacheTimeout);
timeoutRegistered = true;
}
void IpfixSender::registerBeatTimeout()
{
timespec to;
addToCurTime(&to, 100);
timer->addTimeout(this, to, &timeoutIpfixlolibBeat);
}
/**
*
*/
void IpfixSender::notifyQueueRunning() {
registerBeatTimeout();
}
/**

View File

@ -42,14 +42,23 @@ using namespace std;
class IpfixSender : public Module, public Source<NullEmitable*>, public IpfixRecordDestination, public Notifiable
{
public:
IpfixSender(uint32_t observationDomainId, uint32_t maxRecordRate, uint32_t sctpDataLifetime, uint32_t sctpReconnectInterval,
uint32_t templateRefreshInterval, uint32_t templateRefreshRate);
IpfixSender(uint32_t observationDomainId, uint32_t maxRecordRate,
uint32_t sctpDataLifetime,
uint32_t sctpReconnectInterval,
uint32_t templateRefreshInterval,
const std::string &certificateChainFile,
const std::string &privateKeyFile,
const std::string &caFile,
const std::string &caPath);
IpfixSender(uint32_t observationDomainId);
virtual ~IpfixSender();
void addCollector(const char *ip, uint16_t port, uint16_t proto);
void addCollector(const char *ip, uint16_t port,
ipfix_transport_protocol proto, void *aux_config);
void flushPacket();
virtual void notifyQueueRunning();
virtual void onTemplate(IpfixTemplateRecord* record);
virtual void onTemplateDestruction(IpfixTemplateDestructionRecord* record);
virtual void onDataRecord(IpfixDataRecord* record);
@ -69,18 +78,23 @@ protected:
private:
enum SendPolicy {
IfFull,
IfNotEmpty,
Always
};
void performShutdown();
static void* threadWrapper(void* instance);
void processLoop();
void startDataSet(uint16_t templateId);
void endAndSendDataSet();
void sendRecords(SendPolicy policy = IfFull);
void startDataSet(uint16_t templateId, uint16_t dataLength);
void setTemplateId(TemplateInfo::TemplateId templateId,
uint16_t dataLength);
void endDataSet();
void send();
void sendRecords(SendPolicy policy);
void removeRecordReferences();
void registerTimeout();
void onSendRecordsTimeout(void);
void onBeatTimeout(void);
void registerBeatTimeout();
TemplateInfo::TemplateId getUnusedTemplateId();
@ -104,9 +118,11 @@ private:
// send after timeout parameters
queue<IpfixRecord*> recordsToRelease;
uint16_t noCachedRecords; /**< number of records already passed to ipfixlob, should be equal to recordsToRelease.size() */
uint16_t noRecordsInCurrentSet; /**< Number of records in current data set. */
uint16_t recordCacheTimeout; /**< how long may records be cached until sent, milliseconds */
bool timeoutRegistered; /**< true if next timeout was already registered in timer */
uint16_t currentTemplateId; /**< Template ID of the unfinished data set */
uint16_t remainingSpace; /**< Remaining space in current IPFIX message measured in bytes. */
uint16_t ringbufferPos; /**< Pointer to next free slot in @c conversionRingbuffer. */
uint8_t conversionRingbuffer[65536]; /**< Ringbuffer used to store converted imasks between @c ipfix_put_data_field() and @c ipfix_send() */
@ -116,6 +132,9 @@ private:
uint32_t recordsSentStep; /**< number of records sent in timestep (usually 100ms)*/
uint32_t maxRecordRate; /** maximum number of records per seconds to be sent over the wire */
int timeoutSendRecords; /**< Dummy variable. Used as a pointer destination to distinguish between two dirrent types of timeout */
int timeoutIpfixlolibBeat; /**< Dummy variable. Used as a pointer destination to distinguish between two dirrent types of timeout */
// mapping of uniqueId to templateId and vice versa
std::map<TemplateInfo::TemplateId, uint16_t> templateIdToUniqueId; /**< stores uniqueId for a give Template ID */
std::map<uint16_t, TemplateInfo::TemplateId> uniqueIdToTemplateId; /**< stores Template ID for a give unique ID */

View File

@ -135,6 +135,7 @@ Observer::~Observer()
free(captureInterface);
delete[] filter_exp;
if (fileName) { free(fileName); fileName = NULL; }
msg(MSG_DEBUG, "successful shutdown");
}
/*

View File

@ -28,7 +28,7 @@
PSAMPExporterCfg::PSAMPExporterCfg(XMLElement* elem)
: CfgHelper<PSAMPExporterModule, PSAMPExporterCfg>(elem, "psampExporter"),
templateRefreshTime(0), templateRefreshRate(0),
templateRefreshTime(0), /* templateRefreshRate(0), */
maxPacketSize(0), exportDelay(0), reporting(NULL)
{
if (!elem) return;
@ -51,7 +51,7 @@ PSAMPExporterCfg::PSAMPExporterCfg(XMLElement* elem)
} else if (e->matches("udpTemplateManagement")) {
// use 0 as default values for both if the config entry isn't found
templateRefreshTime = getTimeInUnit("templateRefreshTimeout", SEC, IS_DEFAULT_TEMPLATE_TIMEINTERVAL, e);
templateRefreshRate = getInt("templateRefreshRate", IS_DEFAULT_TEMPLATE_RECORDINTERVAL, e);
/* templateRefreshRate = getInt("templateRefreshRate", IS_DEFAULT_TEMPLATE_RECORDINTERVAL, e); */ /* TODO */
} else if (e->matches("collector")) {
collectors.push_back(new CollectorCfg(e));
} else if (e->matches("packetReporting")) {
@ -112,7 +112,7 @@ PSAMPExporterModule* PSAMPExporterCfg::createInstance()
msg(MSG_INFO, "Set maximum export timeout to %d", exportDelay);
instance->setExportTimeout(exportDelay);
}
if (templateRefreshTime || templateRefreshRate) {
if (templateRefreshTime /* || templateRefreshRate */) {
msg(MSG_DIALOG, "Exporter: Configuration of templateRefreshRate/Time not yet supported.");
}
for (unsigned i = 0; i != collectors.size(); ++i) {

View File

@ -52,7 +52,7 @@ private:
/** udpTemplateManagement */
unsigned templateRefreshTime;
unsigned templateRefreshRate;
/* unsigned templateRefreshRate; */ /* TODO */
/** packet restrictions */
uint16_t maxPacketSize;

View File

@ -44,14 +44,14 @@ PSAMPExporterModule::PSAMPExporterModule(Template *tmpl, uint32_t observationDom
// generate the ipfix template
tmplid = templ->getTemplateID();
ret = ipfix_start_template_set(exporter, tmplid, templ->getFieldCount());
ret = ipfix_start_template(exporter, tmplid, templ->getFieldCount());
for(i = 0; i < templ->getFieldCount(); i++) {
templ->getFieldInfo(i, &ttype, &tlength, &toffset, &theader);
ipfix_put_template_field(exporter, tmplid, ttype.id, tlength, ttype.enterprise);
}
ipfix_end_template_set(exporter, tmplid);
ipfix_end_template(exporter, tmplid);
}
PSAMPExporterModule::~PSAMPExporterModule()
@ -176,20 +176,10 @@ void PSAMPExporterModule::flushPacketStream() {
pckCount = 0;
}
bool PSAMPExporterModule::addCollector(const char *address, uint16_t port, uint16_t protocol)
bool PSAMPExporterModule::addCollector(const char *address, uint16_t port, ipfix_transport_protocol protocol)
{
DPRINTF("Adding %i://%s:%d", protocol, address, port);
switch(protocol) {
case 132:
return(ipfix_add_collector(exporter, address, port, SCTP) == 0);
break;
case 17:
return(ipfix_add_collector(exporter, address, port, UDP) == 0);
break;
default:
msg(MSG_ERROR, "PSAMPExporter: Transport protocol %u not supported", protocol);
return false;
}
return(ipfix_add_collector(exporter, address, port, protocol, NULL) == 0);
}
void PSAMPExporterModule::receive(Packet* p)

View File

@ -65,7 +65,7 @@ public:
return true;
}
bool addCollector(const char *address, uint16_t port, uint16_t protocol);
bool addCollector(const char *address, uint16_t port, ipfix_transport_protocol protocol);
private:

View File

@ -78,7 +78,7 @@ bool StateConnectionFilter::processPacket(Packet* p, bool connFilterResult)
// unknown connection
return false;
} else if (exportList[key] > 0) {
bool ret = exportList[key]>(int)payloadLen?true:false;
bool ret = exportList[key]>static_cast<int>(payloadLen)?true:false;
DPRINTF("StateConnectionFilter: Connection known, exporting packet");
exportList[key] -= payloadLen;
DPRINTF("StateConnectionFilter: We have to export %i bytes after exporting this packet", exportList[key]>0?exportList[key]:0);

View File

@ -2,9 +2,23 @@ ADD_EXECUTABLE(test_everything
test_everything.cc
)
ADD_EXECUTABLE(mtutest
mtutest.c
)
TARGET_LINK_LIBRARIES(mtutest
ipfixlolib
common
dl
)
SET_TARGET_PROPERTIES(mtutest
PROPERTIES
LINKER_LANGUAGE CXX)
TARGET_LINK_LIBRARIES(test_everything
ipfixlolib
common
dl
)
ADD_EXECUTABLE(example_code
@ -14,4 +28,22 @@ ADD_EXECUTABLE(example_code
TARGET_LINK_LIBRARIES(example_code
ipfixlolib
common
dl
)
ADD_EXECUTABLE(example_code_2
example_code_2.c
)
TARGET_LINK_LIBRARIES(example_code_2
ipfixlolib
common
dl
)
IF (SUPPORT_DTLS)
TARGET_LINK_LIBRARIES(test_everything ${OPENSSL_LIBRARIES})
TARGET_LINK_LIBRARIES(mtutest ${OPENSSL_LIBRARIES})
TARGET_LINK_LIBRARIES(example_code ${OPENSSL_LIBRARIES})
TARGET_LINK_LIBRARIES(example_code_2 ${OPENSSL_LIBRARIES})
ENDIF (SUPPORT_DTLS)

View File

@ -94,10 +94,13 @@ int main(int argc, char **argv)
coll_ip4_addr : the collector's ipv4 address (in dot notation, e.g. "123.123.123.123")
coll_port: port number of the collector
proto: transport protocol to use, TCP/UDP/SCTP
*aux_config: protocol dependent parameters
You can add up to IPFIX_MAX_COLLECTORS collectors.
*/
ret = ipfix_add_collector(my_exporter, collector_ip, collector_port, UDP);
ipfix_aux_config_udp aux_config;
aux_config.mtu = 1500;
ret = ipfix_add_collector(my_exporter, collector_ip, collector_port, UDP, &aux_config);
printf("ipfix_add_collector returned %i\n", ret);
/*
@ -127,7 +130,7 @@ int main(int argc, char **argv)
template_id: an ID for this template
field_count: # of entries/fields
*/
ret=ipfix_start_template_set(my_exporter, my_template_id, 6);
ret=ipfix_start_template(my_exporter, my_template_id, 6);
/*
Add fields to the exporter.
@ -146,7 +149,7 @@ int main(int argc, char **argv)
ret=ipfix_put_template_field(my_exporter, my_template_id, 2, 8, 0);
/* Finalize the template */
ret=ipfix_end_template_set(my_exporter, my_template_id);
ret=ipfix_end_template(my_exporter, my_template_id);
/* Add another template */
@ -160,7 +163,7 @@ int main(int argc, char **argv)
template_id: an ID for this template
field_count: # of entries/fields
*/
ret=ipfix_start_template_set(my_exporter, my_template_id2, 4);
ret=ipfix_start_template(my_exporter, my_template_id2, 4);
/*
Add fields to the exporter.
@ -177,7 +180,7 @@ int main(int argc, char **argv)
ret=ipfix_put_template_field(my_exporter, my_template_id2, 11, 2, 0);
/* Finalize the template */
ret=ipfix_end_template_set(my_exporter, my_template_id2);
ret=ipfix_end_template(my_exporter, my_template_id2);
/*
Main exporting loop

View File

@ -0,0 +1,639 @@
/* vim: set sts=4 sw=4 cindent nowrap: This modeline was added by Daniel Mentz */
/*
This file is part of IPFIXLOLIB
EXAMPLE CODE
Published under GPL v2
Ronny T. Lampert, 2005-01
Daniel Mentz, 2010-04
based upon the original IPFIXLOLIB
by Jan Petranek, University of Tuebingen
2004-11-18
jan@petranek.de
*/
#include <stdio.h>
#include <getopt.h>
#include "common/ipfixlolib/ipfixlolib.h"
#include "common/ipfixlolib/encoding.h"
#include "common/msg.h"
#define MY_OBSERVATION_DOMAIN_ID 70538
#define DEFAULT_COLLECTOR_IP_ADDRESS "127.0.0.1"
struct config {
const char *coll_ip4_addr;
int coll_port;
enum ipfix_transport_protocol transport_protocol;
uint16_t mtu;
const char *peer_fqdn;
const char *certificate_chain_file;
const char *private_key_file;
const char *ca_file;
const char *ca_path;
};
struct config myconfig = {
.coll_ip4_addr = DEFAULT_COLLECTOR_IP_ADDRESS,
.coll_port = 0,
.transport_protocol = UDP,
.mtu = 0,
.peer_fqdn = 0,
.certificate_chain_file = 0,
.private_key_file = 0,
.ca_file = 0,
.ca_path = 0
};
/*
Data we want to transmit.
NOTE: User-data must be in network byte order for interoperability
*/
typedef struct {
uint32_t ip_src_addr;
uint32_t ip_dst_addr;
uint16_t src_port;
uint16_t dst_port;
uint64_t byte_count;
uint64_t packet_count;
} meter_data;
meter_data my_meter_data[10000];
int my_meter_data_next_free = 0;
int parse_command_line_arguments(int argc, char **argv);
int add_collector(ipfix_exporter *exporter);
int get_sample_data1(meter_data *mdat);
int get_sample_data2(meter_data *mdat);
int set_config_on_exporter(ipfix_exporter *exporter);
void print_usage(char *argv0);
/*
You can add up to IPFIX_MAX_COLLECTORS collectors.
*/
int add_collector(ipfix_exporter *exporter) {
int ret;
void *aux_config = NULL;
ipfix_aux_config_udp acu = {
.mtu = myconfig.mtu
};
ipfix_aux_config_dtls_over_udp acdou = {
.udp = { .mtu = myconfig.mtu},
.dtls = { .peer_fqdn = myconfig.peer_fqdn}
};
ipfix_aux_config_dtls_over_sctp acdos = {
.dtls = { .peer_fqdn = myconfig.peer_fqdn}
};
if (myconfig.transport_protocol == UDP) {
aux_config = &acu;
} else if (myconfig.transport_protocol == DTLS_OVER_UDP) {
aux_config = &acdou;
} else if (myconfig.transport_protocol == DTLS_OVER_SCTP) {
aux_config = &acdos;
}
/* The type of the last parameter to ipfix_add_collector() depends
* on the transport protocol that has been chose. */
if ((ret = ipfix_add_collector(exporter, myconfig.coll_ip4_addr,
myconfig.coll_port, myconfig.transport_protocol, aux_config))) {
fprintf(stderr, "ipfix_add_collector() failed.\n");
return -1;
}
printf("Added collector.\n");
/* ipfix_add_collector() never blocks but returns immediately. As a
* consequence, it can only kick of the DTLS handshake, but it can't
* wait until it completed (or failed). ipfix_beat() has to be called
* regularly to allow ipfixlolib to proceed with the DTLS handshake.
* ipfix_beat() returns 0 if there are no more pending handshakes. */
while(ipfix_beat(exporter))
usleep(10000);
return 0;
}
meter_data *alloc_meter_data(void) {
if (my_meter_data_next_free >= sizeof(my_meter_data) / sizeof(my_meter_data[0])) {
fprintf(stderr,"No more memory to store metering data.\n");
abort();
}
return &my_meter_data[my_meter_data_next_free++];
}
/* generate constant data for testing / example purposes. It is important to
* understand that all of this data has to be in *network byte order*.
* ipfixlolib will not perform this transformation for you. */
int get_sample_data1(meter_data *mdat) {
mdat->ip_src_addr = htonl(0x01020304); // 1.2.3.4
mdat->ip_dst_addr = htonl(0x02040608); // 2.4.6.8
mdat->src_port = htons(12);
mdat->dst_port = htons(13);
mdat->byte_count = htonll(1567490);
mdat->packet_count = htonll(42);
return 0;
}
/* generate some other data. Same caveat applies as to get_sample_data1:
*
* The data has to be in network byte order. */
int get_sample_data2(meter_data *mdat) {
mdat->ip_src_addr = htonl(0x01020304); // 1.2.3.4
mdat->ip_dst_addr = htonl(0x02040608); // 2.4.6.8
mdat->src_port = htons(22);
mdat->dst_port = htons(4713);
mdat->byte_count = htonll(3);
mdat->packet_count = htonll(2);
return 0;
}
int main(int argc, char **argv)
{
int ret =0;
if (parse_command_line_arguments(argc,argv)) {
exit(EXIT_FAILURE);
}
/* Initialize the exporter.
* MY_OBSERVATION_ID is the Observation ID that will be sent
* with every IPFIX Message. */
ipfix_exporter *my_exporter;
ret=ipfix_init_exporter(MY_OBSERVATION_DOMAIN_ID, &my_exporter);
if (set_config_on_exporter(my_exporter))
exit(EXIT_FAILURE);
if (ret) {
fprintf(stderr, "ipfix_init_exporter failed!\n");
exit(EXIT_FAILURE);
}
printf("Initialized exporter.\n");
if (add_collector(my_exporter)) exit(EXIT_FAILURE);
printf("++ Defining Templates ++.\n");
/*
* Prior to sending Data Records we have to define a Template that describes the
* encoding of these Data Records.
* We choose 12345 as the Template ID.
The template shall contain the following fields:
# | IPFIX name of field | IPFIX field ID | length of associated data type
-------------------------------------------------------------------------------
1 | sourceAddressV4 | 8 | 4
2 | destinationAddressV4 | 12 | 4
3 | transportSourcePort | 7 | 2
4 | transportDestinationPort | 11 | 2
5 | deltaOctetCount | 1 | 8
6 | deltaPacketCount | 2 | 8
*/
uint16_t my_template1_id = 12345;
uint16_t my_n_template1_id = htons(my_template1_id); /* Template ID in network byte order */
uint16_t my_template1_length = 4+4+2+2+8+8; /* cumulative length of all Template fields */
/*
Now start the adding of fields.
exporter: the exporter
template_id: an ID for this template
field_count: # of entries/fields
*/
printf("Starting template with id %d.\n",my_template1_id);
ret = ipfix_start_template(my_exporter, my_template1_id, 6);
/*
Add fields to the exporter.
exporter: the exporter
template_id: the template ID chosen beforehand
type: the IPFIX field ID for this entry
length: sizeof() data type
enterprise: FIXME ???
*/
printf("Putting data fields.\n");
ret = ipfix_put_template_field(my_exporter, my_template1_id, 8, 4, 0);
ret = ipfix_put_template_field(my_exporter, my_template1_id, 12, 4, 0);
ret = ipfix_put_template_field(my_exporter, my_template1_id, 7, 2, 0);
ret = ipfix_put_template_field(my_exporter, my_template1_id, 11, 2, 0);
ret = ipfix_put_template_field(my_exporter, my_template1_id, 1, 8, 0);
ret = ipfix_put_template_field(my_exporter, my_template1_id, 2, 8, 0);
/* Finalize the template */
printf("Ending template.\n");
ret = ipfix_end_template(my_exporter, my_template1_id);
/*
* We decide to define another Template with ID 6789.
The template shall contain the following fields:
# | IPFIX name of field | IPFIX field ID | length of associated data type
-------------------------------------------------------------------------------
1 | sourceAddressV4 | 8 | 4
2 | destinationAddressV4 | 12 | 4
3 | transportSourcePort | 7 | 2
4 | transportDestinationPort | 11 | 2
*/
uint16_t my_template2_id = 6789;
uint16_t my_n_template2_id = htons(my_template2_id); /* Same Template ID in network byte order */
/* uint16_t my_template2_length = 4+4+2+2; */ /* cumulative length of all Template fields */
/*
Now start the adding of fields.
exporter: the exporter
template_id: an ID for this template
field_count: # of entries/fields
*/
printf("Starting template with id %d.\n",my_template2_id);
ret = ipfix_start_template(my_exporter, my_template2_id, 4);
/*
Add fields to the exporter.
exporter: the exporter
template_id: the template ID chosen beforehand
type: the IPFIX field ID for this entry
length: sizeof() data type
enterprise: FIXME ???
*/
printf("Putting data fields.\n");
ret = ipfix_put_template_field(my_exporter, my_template2_id, 8, 4, 0);
ret = ipfix_put_template_field(my_exporter, my_template2_id, 12, 4, 0);
ret = ipfix_put_template_field(my_exporter, my_template2_id, 7, 2, 0);
ret = ipfix_put_template_field(my_exporter, my_template2_id, 11, 2, 0);
/* Finalize the template */
printf("Ending template.\n");
ret = ipfix_end_template(my_exporter, my_template2_id);
printf("++ Sending Data ++.\n");
/*
Main exporting loop
What you basically do is
1) get data
2) start a data set
3) add the fields
4) finish data set
5) send data
*/
meter_data *my_md;
int sp;
int num_records = 0; /* Number of records in the current Data Set. We have
to pass this value when we call
ipfix_end_data_set(). If num_records == 0, then
there's no open Data Set. We have to call
ipfix_start_data_set() first in this situation. */
int records_per_set = 50; /* We don't want to store more than this number
of records in one Data Set. */
int total_records = 1000; /* Total number of records we want to send */
int i;
for(i=0;i<total_records;i++) {
/* How many bytes still fit in the IPFIX Message? */
sp = ipfix_get_remaining_space(my_exporter);
printf("Remaining available space in IPFIX Message: %d\n", sp);
/* We don't want to have more than records_per_set Records in one Data Set */
if (num_records == records_per_set) {
printf("There are now %d Records in this Data Set. Let's close this and start a new one.\n",num_records);
printf("ipfix_end_data_set()\n");
ret = ipfix_end_data_set(my_exporter,num_records);
if (ret != 0)
fprintf(stderr, "ipfix_end_data_set failed!\n");
/* Reset num_records to 0 */
num_records = 0;
}
/* There's an open Data Set (i.e. num_records > 0), but there's *not*
* enough space left to fit another Data Record. */
if (num_records > 0 &&
(sp=ipfix_get_remaining_space(my_exporter)) < my_template1_length) {
printf("Remaining space: %d < %d. Too small to fit another Data Record\n",
sp,my_template1_length);
/* Close Data Set */
printf("ipfix_end_data_set()\n");
ret = ipfix_end_data_set(my_exporter,num_records);
num_records = 0;
if (ret != 0)
fprintf(stderr, "ipfix_end_data_set failed!\n");
/* Send off data. As a result, the send buffer will be empty again. */
printf("ipfix_send()\n");
ret = ipfix_send(my_exporter);
if (ret != 0)
fprintf(stderr, "ipfix_send failed!\n");
/* Now, we can recycle the memory in which we stored the data. */
my_meter_data_next_free = 0;
/* Now, there should be plenty of space available. */
sp = ipfix_get_remaining_space(my_exporter);
printf("Remaining available space in IPFIX Message after ipfix_send(): %d\n", sp);
}
/* If there's no open Data Set */
if (num_records == 0) {
/* Make sure that at least the set header plus
* one data record fits inside. */
sp = ipfix_get_remaining_space(my_exporter);
if (sp < IPFIX_OVERHEAD_PER_SET + my_template1_length) {
/* If not, clear the send buffer by sending out the data that
* sits in the send buffer. */
printf("Can't start data set. No more space in IPFIX Message. Space left %d\n",sp);
printf("ipfix_send()\n");
ret = ipfix_send(my_exporter);
if (ret != 0)
fprintf(stderr, "ipfix_send failed!\n");
/* Now, we can recycle the memory in which we stored the data. */
my_meter_data_next_free = 0;
/* Now we should have plenty of remaining space. */
sp = ipfix_get_remaining_space(my_exporter);
printf("Remaining available space in IPFIX Message after ipfix_send(): %d\n", sp);
if (sp < IPFIX_OVERHEAD_PER_SET + my_template1_length) {
/* If the buffer is still too small so that not even
* a single Data Record fits in, then something is
* severely wrong */
printf("Can't start data set. No more space in IPFIX Message even though we just called ipfix_send()\n");
goto out;
}
}
/* start a data-set.
* NOTE: The Template ID has to be passed in *network byte order*.
* This is in contrast to the corresponding parameter of
* ipfix_start_template()
* */
printf("ipfix_start_data_set()\n");
ret = ipfix_start_data_set(my_exporter, my_n_template1_id);
if (ret != 0 ) {
// do not try use ipfix_put_data_field or ipfix_put_end_field,
// if ret=ipfix_start_data_set has failed!
fprintf(stderr, "ipfix_start_data_set failed!\n");
goto out;
}
/* Remaining space should now be 4 bytes less than before because the
* Data Set header is 4 bytes long. */
sp = ipfix_get_remaining_space(my_exporter);
printf("Remaining available space in IPFIX Message after ipfix_start_data_set(): %d\n", sp);
}
my_md = alloc_meter_data();
/* get data - must be in Network Byte Order for interoperability */
if (num_records % 2)
get_sample_data2(my_md);
else
get_sample_data1(my_md);
/*
now fill the pre-defined data fields
NOTE: supplied data is NOT copied and has to
stay valid until the ipfix_send() below!
NOTE: It's the user's responsibility to ensure that
the added data is conform to the indicated template.
*/
printf("Passing flow data to ipfixlolib.\n");
ipfix_put_data_field(my_exporter, &my_md->ip_src_addr, 4);
ipfix_put_data_field(my_exporter, &my_md->ip_dst_addr, 4);
ipfix_put_data_field(my_exporter, &my_md->src_port, 2);
ipfix_put_data_field(my_exporter, &my_md->dst_port, 2);
ipfix_put_data_field(my_exporter, &my_md->byte_count, 8);
ipfix_put_data_field(my_exporter, &my_md->packet_count, 8);
num_records++;
}
printf("ipfix_end_data_set()\n");
ret = ipfix_end_data_set(my_exporter,num_records);
num_records = 0;
if (ret != 0)
fprintf(stderr, "ipfix_end_data_set failed!\n");
printf("ipfix_send()\n");
ret = ipfix_send(my_exporter);
if (ret != 0)
fprintf(stderr, "ipfix_send failed!\n");
/* Now, we can recycle the memory in which we stored the data. */
my_meter_data_next_free = 0;
/* start a data-set for second template*/
ret=ipfix_start_data_set(my_exporter, my_n_template2_id);
if (ret != 0 ) {
// do not try use ipfix_put_data_field or ipfix_put_end_field,
// if ret=ipfix_start_data_set has failed!
fprintf(stderr, "ipfix_start_data_set failed!\n");
goto out;
}
my_md = alloc_meter_data();
/* get data - must be in Network Byte Order for interoperability */
get_sample_data1(my_md);
/*
now fill the pre-defined data fields
*/
ipfix_put_data_field(my_exporter, &my_md->ip_src_addr, 4);
/* We changed our mind and want to stop, so call ipfix_cancel_data_set
instead of ipfix_end_data_set */
ret=ipfix_cancel_data_set(my_exporter);
if (ret != 0)
fprintf(stderr, "ipfix_end_data_set failed!\n");
/* start again for second template*/
ret=ipfix_start_data_set(my_exporter, my_n_template2_id);
if (ret != 0 ) {
// do not try use ipfix_put_data_field or ipfix_put_end_field,
// if ret=ipfix_start_data_set has failed!
fprintf(stderr, "ipfix_start_data_set failed!\n");
goto out;
}
my_md = alloc_meter_data();
/* get data - must be in Network Byte Order for interoperability */
get_sample_data1(my_md);
/*
now fill the pre-defined data fields
*/
ipfix_put_data_field(my_exporter, &my_md->ip_src_addr, 4);
ipfix_put_data_field(my_exporter, &my_md->ip_dst_addr, 4);
/* Set marker in order to go back */
ipfix_set_data_field_marker(my_exporter);
ipfix_put_data_field(my_exporter, &my_md->src_port, 2);
/* Go back to the marker */
ipfix_delete_data_fields_upto_marker(my_exporter);
/* It was just a joke, so let's put the data field again */
ipfix_put_data_field(my_exporter, &my_md->src_port, 2);
ipfix_put_data_field(my_exporter, &my_md->dst_port, 2);
/* finish the data set
remark: the main task of ipfix_end_data_set is to calculate the
length of the data set */
ret = ipfix_end_data_set(my_exporter,1);
if (ret != 0)
fprintf(stderr, "ipfix_end_data_set failed!\n");
/*
send the data-set(s) template sending is handled entirely by the
library, too.
NOTE: ALL DATA added via ipfix_put_data_field has to stay valid
until ipfix_send() returns.
*/
ret = ipfix_send(my_exporter);
if (ret != 0)
fprintf(stderr, "ipfix_send failed!\n");
/* Now, we can recycle the memory in which we stored the data. */
my_meter_data_next_free = 0;
out:
/* if you no longer need the exporter: free resources */
ret=ipfix_remove_collector(my_exporter, myconfig.coll_ip4_addr, myconfig.coll_port);
ipfix_deinit_exporter(my_exporter);
printf("Done.\n");
exit(EXIT_SUCCESS);
}
int parse_command_line_arguments(int argc, char **argv) {
int transport = UDP;
int debug_level = MSG_ERROR;
enum opts { mtu=1, peer_fqdn,cert,key,CAfile,CApath,collector,port };
struct option long_options[] = {
{"udp", no_argument, &transport, UDP},
{"sctp", no_argument, &transport, SCTP},
{"dtls_over_udp", no_argument, &transport, DTLS_OVER_UDP},
{"dtls_over_sctp", no_argument, &transport, DTLS_OVER_SCTP},
{"mtu",required_argument,0,mtu},
{"peer_fqdn",required_argument,0,peer_fqdn},
{"cert",required_argument,0,cert},
{"key",required_argument,0,key},
{"CAfile",required_argument,0,CAfile},
{"CApath",required_argument,0,CApath},
{"collector",required_argument,0,collector},
{"port",required_argument,0,port},
{"help",no_argument,0,'h'},
{0, 0, 0, 0}
};
while (1) {
int c;
int option_index = 0;
long l;
char *endptr;
struct in_addr tmpaddr;
c = getopt_long (argc, argv, "dh", long_options, &option_index);
if (c == -1) break;
switch (c) {
case 0:
break;
case 'h':
print_usage(argv[0]);
exit(EXIT_SUCCESS);
case 'd':
debug_level++;
break;
case mtu:
l = strtol(optarg,&endptr,0);
if (*endptr != '\0' || l < 576 || l > UINT16_MAX) {
fprintf(stderr,"bad mtu value\n");
return -1;
}
myconfig.mtu = l;
break;
case peer_fqdn:
myconfig.peer_fqdn = optarg;
break;
case cert:
myconfig.certificate_chain_file = optarg;
break;
case key:
myconfig.private_key_file = optarg;
break;
case CAfile:
myconfig.ca_file = optarg;
break;
case CApath:
myconfig.ca_path = optarg;
break;
case collector:
if (inet_pton(AF_INET,optarg,&tmpaddr) != 1) {
fprintf(stderr,"bad IP address\n");
return -1;
}
myconfig.coll_ip4_addr = optarg;
break;
case port:
l = strtol(optarg,&endptr,0);
if (*endptr != '\0' || l < 0 || l > UINT16_MAX) {
fprintf(stderr,"bad port number\n");
return -1;
}
myconfig.coll_port = l;
break;
}
}
if (optind != argc) {
fprintf(stderr,"invalid arguments\n");
return -1;
}
myconfig.transport_protocol = transport;
if (myconfig.coll_port == 0) {
if (myconfig.transport_protocol == DTLS_OVER_UDP ||
myconfig.transport_protocol == DTLS_OVER_SCTP)
myconfig.coll_port = 4740; /* default port for secure connections */
else
myconfig.coll_port = 4739; /* default port */
}
msg_setlevel(debug_level);
return 0;
}
int set_config_on_exporter(ipfix_exporter *exporter) {
int ret = 0;
ret = ipfix_set_ca_locations(exporter, myconfig.ca_file, myconfig.ca_path);
if (ret) return ret;
if (myconfig.certificate_chain_file)
ret = ipfix_set_dtls_certificate(exporter, myconfig.certificate_chain_file, myconfig.private_key_file);
return ret;
}
void print_usage(char *argv0) {
printf(
"Usage: %s [OPTIONS]\n"
" --collector IPADDR connect to collector at IP address IPADDR.\n"
" Default is 127.0.0.1\n"
" --port P use port P instead of standard port number\n"
" which is 4739 or 4740 if DTLS is used.\n"
" --udp use UDP as transport protocol (default)\n"
" --sctp use SCTP as transport protocol\n"
" --dtls_over_udp use DTLS over UDP as transport protocol\n"
" --dtls_over_sctp use DTLS over SCTP as transport protocol\n"
" --mtu V set maximum transmission unit (MTU) to V\n"
"The following options apply to DTLS only\n"
" --peer_fqdn N peer must present certificate which contains\n"
" the FQDN N.\n"
" --cert F X.509 certificate chain in PEM format is stored in F\n"
" --key F private key for certificate is stored in F\n"
" --CAfile F search F for CA certificates\n"
" --CApath D search directory D for CA certificates\n"
,
argv0
);
}

View File

@ -0,0 +1,89 @@
#include <stdio.h>
#include "common/ipfixlolib/ipfixlolib.h"
#include "common/ipfixlolib/ipfix.h"
#include "common/msg.h"
#define OBSERVATION_DOMAIN_ID 1
#define TEMPLATE_ID 260
#define COLLECTOR_IP_ADDRESS "127.0.0.1"
// #define COLLECTOR_IP_ADDRESS "87.230.53.17"
// #define COLLECTOR_IP_ADDRESS "8.8.8.8"
#define MTU 0
void define_template(ipfix_exporter *exporter) {
ipfix_start_template(exporter, TEMPLATE_ID, 1); // field_count == 1
ipfix_put_template_field(exporter, TEMPLATE_ID, IPFIX_TYPEID_sourceIPv4Mask, 1, 0); // length == 1, enterprise_id == 0
ipfix_end_template(exporter, TEMPLATE_ID );
}
void put_data(ipfix_exporter *exporter) {
uint8_t data[256];
uint8_t i = 0;
int j;
int s;
for (j=0;j<sizeof(data);j++) {
data[j] = j;
}
ipfix_start_data_set(exporter, htons(TEMPLATE_ID));
while ((s = ipfix_get_remaining_space(exporter))){
if (i == 0) {
ipfix_end_data_set(exporter, 1);
if (s>IPFIX_OVERHEAD_PER_SET) {
printf("Starting new data set. Remaining space: %d\n",s);
ipfix_start_data_set(exporter, htons(TEMPLATE_ID));
} else {
printf("Can't start new data set. Remaining Space: %d\n",s);
break;
}
}
if (ipfix_put_data_field(exporter,&data[i], sizeof(*data))) break;
i++;
}
ipfix_end_data_set(exporter, 1);
ipfix_send(exporter);
}
void add_udp_collector(ipfix_exporter *exporter) {
ipfix_aux_config_udp acu = {
.mtu = 0
};
if (ipfix_add_collector(exporter, COLLECTOR_IP_ADDRESS, 4739, UDP, &acu)) {
fprintf(stderr, "ipfix_add_collector() failed.\n");
exit(1);
}
}
void add_dtls_over_udp_collector(ipfix_exporter *exporter) {
ipfix_aux_config_dtls_over_udp acu = {
.udp = { .mtu = MTU},
.dtls = { .peer_fqdn = NULL}
};
if (ipfix_add_collector(exporter, COLLECTOR_IP_ADDRESS, 4740, DTLS_OVER_UDP, &acu)) {
fprintf(stderr, "ipfix_add_collector() failed.\n");
exit(1);
}
while(ipfix_beat(exporter))
usleep(10000);
}
int main(int argc, char **argv) {
int i;
ipfix_exporter *exporter;
msg_setlevel(MSG_VDEBUG);
if (ipfix_init_exporter(OBSERVATION_DOMAIN_ID, &exporter)) {
fprintf(stderr, "ipfix_init_exporter() failed.\n");
exit(1);
};
// add_dtls_over_udp_collector(exporter);
add_udp_collector(exporter);
define_template(exporter);
for(i=0;i<3;i++) {
put_data(exporter);
sleep(2);
}
ipfix_remove_collector(exporter, COLLECTOR_IP_ADDRESS, 4740);
ipfix_deinit_exporter(exporter);
return 0;
}

View File

@ -74,9 +74,9 @@ int main(int argc, char *argv[])
ret=0;
ret|=ipfix_start_template_set(my_exporter, create_id, 1);
ret|=ipfix_start_template(my_exporter, create_id, 1);
ret|=ipfix_put_template_field(my_exporter, create_id, 2, 8, 0);
ret|=ipfix_end_template_set(my_exporter, create_id);
ret|=ipfix_end_template(my_exporter, create_id);
if (ret != 0) {
fprintf(stderr, "create template failed!\n");
@ -90,9 +90,9 @@ int main(int argc, char *argv[])
my_template_id=4711;
ret=0;
ret|=ipfix_start_template_set(my_exporter, my_template_id, 1);
ret|=ipfix_start_template(my_exporter, my_template_id, 1);
ret|=ipfix_put_template_field(my_exporter, my_template_id, 2, 8, 0);
ret|=ipfix_end_template_set(my_exporter, my_template_id);
ret|=ipfix_end_template(my_exporter, my_template_id);
if (ret != 0) {
fprintf(stderr, "create template failed!\n");
@ -113,7 +113,7 @@ int main(int argc, char *argv[])
#ifdef SUPPORT_SCTP
case 'c':
// add SCTP collector
ret=ipfix_add_collector(my_exporter, "127.0.0.1", 1500, SCTP);
ret=ipfix_add_collector(my_exporter, "127.0.0.1", 1500, SCTP, NULL);
if (ret != 0) {
fprintf(stderr, "ipfix_add_collector failed!\n");
@ -124,7 +124,9 @@ int main(int argc, char *argv[])
#endif
case 'u':
// add UDP collector
ret=ipfix_add_collector(my_exporter, "127.0.0.1", 1500, UDP);
ipfix_aux_config_udp aux_config;
aux_config.mtu = 1500;
ret=ipfix_add_collector(my_exporter, "127.0.0.1", 4711, UDP, &aux_config);
if (ret != 0) {
fprintf(stderr, "ipfix_add_collector failed!\n");
exit(-1);
@ -135,10 +137,10 @@ int main(int argc, char *argv[])
assert(scanf("%d",&delete_id)==1);
printf("Start testing Template destruction ID : %d!\n", delete_id);
ret=ipfix_remove_template_set(my_exporter, delete_id);
ret=ipfix_remove_template(my_exporter, delete_id);
if (ret != 0) {
fprintf(stderr, "ipfix_remove_template_set failed!\n");
fprintf(stderr, "ipfix_remove_template failed!\n");
}
break;
case 'r':

View File

@ -175,7 +175,7 @@ static void sig_INT_handler(int x)
static bool shutdownInitiated = false;
if (shutdownInitiated) {
printf("second signal received, shutting down the hard way!");
printf("second signal received, shutting down the hard way!\n");
exit(2);
}

View File

@ -31,6 +31,13 @@ TARGET_LINK_LIBRARIES(testCollector
${PCAP_LIBRARY}
)
IF (SUPPORT_DTLS)
TARGET_LINK_LIBRARIES(testCollector ${OPENSSL_LIBRARIES})
IF (CRYPTO_FOUND)
TARGET_LINK_LIBRARIES(testCollector ${CRYPTO_LIBRARIES})
ENDIF (CRYPTO_FOUND)
ENDIF (SUPPORT_DTLS)
IF (LIBXML2_INCLUDE_DIR AND LIBXML2_LIBRARIES)
MESSAGE(STATUS "Found libxml2 libraries")
INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR})

View File

@ -28,10 +28,10 @@
#include "modules/ipfix/IpfixParser.hpp"
#include "modules/ipfix/IpfixPacketProcessor.hpp"
#include "modules/ipfix/IpfixReceiverUdpIpV4.hpp"
#include "modules/ipfix/IpfixReceiverDtlsUdpIpV4.hpp"
#include "modules/ipfix/IpfixReceiverSctpIpV4.hpp"
#include "modules/ipfix/IpfixPrinter.hpp"
#include "core/ConnectionQueue.h"
#include "common/msg.h"
#include <netinet/in.h>
@ -39,12 +39,23 @@
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <getopt.h>
#define DEFAULT_LISTEN_PORT 1500
void usage()
void usage(const char *argv0)
{
msg(MSG_FATAL, "Usage: testCollector [{udp|sctp} <port>]");
fprintf(stderr,"Usage: %s\n",argv0);
fprintf(stderr," --port,-p Port number to use\n");
fprintf(stderr," --protocol Either udp, sctp or dtls_over_udp\n");
fprintf(stderr," --cert The certificate to use\n");
fprintf(stderr," --key The private key to use\n");
fprintf(stderr," --CAfile A file containing trusted "
"certificates to use during client authentication\n");
fprintf(stderr," --CApath The directory to use for client "
"certificate verification. These are also used "
"when building the server certificate chain.\n");
fprintf(stderr," --peername Expected FQDN of the peer.\n");
}
void sigint(int) {
@ -62,25 +73,84 @@ int main(int argc, char *argv[]) {
int lport = DEFAULT_LISTEN_PORT;
std::string proto = "udp";
std::string certificateChainFile, privateKeyFile, caFile, caPath, peername;
msg_setlevel(MSG_DEBUG);
signal(SIGINT, sigint);
if (!(argc == 1 || argc == 2 || argc == 3)) {
usage();
return -1;
int c;
while (1) {
static struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"port", required_argument, 0, 'p'},
{"protocol", required_argument, 0, 'o'},
{"cert", required_argument, 0, 'c'},
{"key", required_argument, 0, 'k'},
{"CAfile", required_argument, 0, 'a'},
{"CApath", required_argument, 0, 'b'},
{"peername", required_argument, 0, 'n'},
{0,0,0,0}
};
int option_index = 0;
c = getopt_long(argc, argv, "hp:", long_options, &option_index);
if (c==-1) break;
switch(c) {
case 'h':
usage(argv[0]); return -1;
case 'p':
char *endptr;
lport = strtol(optarg,&endptr,10);
if (*endptr != '\0' || lport <= 0 || lport > (1<<16) - 1) {
fprintf(stderr, "illegal port number\n");
usage(argv[0]); return -1;
}
break;
case 'o':
proto = optarg;
break;
case 'c':
certificateChainFile = optarg;
break;
case 'k':
privateKeyFile = optarg;
break;
case 'a':
caFile = optarg;
break;
case 'b':
caPath = optarg;
break;
case 'n':
peername = optarg;
break;
case '?':
break;
default:
abort();
}
}
if (optind != argc) {
usage(argv[0]); return -1;
}
if (argc == 2)
proto = argv[1];
if (argc == 3)
lport = atoi(argv[2]);
IpfixReceiver* ipfixReceiver = 0;
if (proto == "udp") {
msg(MSG_INFO, "Creating UDP listener on port %i", lport);
ipfixReceiver = new IpfixReceiverUdpIpV4(lport);
} else if (proto == "dtls_over_udp") {
#ifdef SUPPORT_DTLS
msg(MSG_INFO, "Creating DTLS over UDP listener on port %i", lport);
ipfixReceiver = new IpfixReceiverDtlsUdpIpV4(lport,"",certificateChainFile,privateKeyFile,caFile,caPath);
#else
msg(MSG_FATAL, "testcollector has been compiled without dtls/openssl support");
return -1;
#endif
} else if (proto == "sctp") {
#ifdef SUPPORT_SCTP
ipfixReceiver = new IpfixReceiverSctpIpV4(lport, "127.0.0.1");