merged DTLS support
git-svn-id: file:///Users/braun/svn/vermont/branches/vermont/dtls-merge@2394 aef3b71b-58ee-0310-9ba9-8811b9f0742fmaster
parent
0934e21030
commit
46f10287be
|
@ -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)
|
||||
|
|
2
INSTALL
2
INSTALL
|
@ -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/>
|
||||
|
||||
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
@ -171,7 +171,6 @@
|
|||
</ipfixPacketRestrictions>
|
||||
<udpTemplateManagement>
|
||||
<templateRefreshTimeout>10</templateRefreshTimeout>
|
||||
<templateRefreshRate>100</templateRefreshRate>
|
||||
</udpTemplateManagement>
|
||||
-->
|
||||
<collector>
|
||||
|
|
|
@ -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-----
|
|
@ -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-----
|
|
@ -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-----
|
|
@ -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-----
|
|
@ -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-----
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
@ -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>
|
|
@ -0,0 +1,11 @@
|
|||
<ipfixConfig>
|
||||
<ipfixCollector id="1">
|
||||
<listener>
|
||||
<transportProtocol>UDP</transportProtocol>
|
||||
</listener>
|
||||
<next>2</next>
|
||||
</ipfixCollector>
|
||||
<ipfixPrinter id="2">
|
||||
</ipfixPrinter>
|
||||
</ipfixConfig>
|
||||
|
|
@ -30,6 +30,8 @@ ADD_LIBRARY(common
|
|||
cryptopan/rijndael.cpp
|
||||
hmacsha1/sha1.cpp
|
||||
hmacsha1/sha1_hmac.cpp
|
||||
openssl/OpenSSL.cpp
|
||||
openssl/SSLCTXWrapper.cpp
|
||||
)
|
||||
|
||||
SUBDIRS(
|
||||
|
|
|
@ -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
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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, ...);
|
||||
|
|
|
@ -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 */
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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_*/
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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!");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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_*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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_*/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -426,7 +426,6 @@ void IpfixPrinter::onTemplateDestruction(IpfixTemplateDestructionRecord* record)
|
|||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* prints a datarecord in a special, easy-to-read data format in one line
|
||||
*/
|
||||
|
|
|
@ -42,6 +42,14 @@ IpfixReceiver::IpfixReceiver()
|
|||
{
|
||||
}
|
||||
|
||||
IpfixReceiver::IpfixReceiver(int port)
|
||||
: exitFlag(true),
|
||||
receiverPort(port),
|
||||
thread(threadWrapper)
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees memory used by an IpfixReceiver.
|
||||
*/
|
||||
|
|
|
@ -40,6 +40,7 @@ class IpfixReceiver
|
|||
{
|
||||
public:
|
||||
IpfixReceiver();
|
||||
IpfixReceiver(int port);
|
||||
virtual ~IpfixReceiver();
|
||||
|
||||
void performStart();
|
||||
|
|
|
@ -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*/
|
|
@ -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
|
||||
|
|
@ -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*/
|
||||
|
|
@ -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
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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 = "");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -135,6 +135,7 @@ Observer::~Observer()
|
|||
|
||||
free(captureInterface);
|
||||
delete[] filter_exp;
|
||||
if (fileName) { free(fileName); fileName = NULL; }
|
||||
msg(MSG_DEBUG, "successful shutdown");
|
||||
}
|
||||
/*
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -52,7 +52,7 @@ private:
|
|||
|
||||
/** udpTemplateManagement */
|
||||
unsigned templateRefreshTime;
|
||||
unsigned templateRefreshRate;
|
||||
/* unsigned templateRefreshRate; */ /* TODO */
|
||||
|
||||
/** packet restrictions */
|
||||
uint16_t maxPacketSize;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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':
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Reference in New Issue