From bac3da61644515f05663789b122554dc77549286 Mon Sep 17 00:00:00 2001 From: Luca Muscariello Date: Thu, 17 Jan 2019 13:47:57 +0100 Subject: This is the first commit of the hicn project Change-Id: I6f2544ad9b9f8891c88cc4bcce3cf19bd3cc863f Signed-off-by: Luca Muscariello --- .gitreview | 4 + AUTHORS | 6 + CMakeLists.txt | 51 + LICENSE | 202 +++ README.md | 33 + apps/README.md | 2 + cmake/Modules/BuildMacros.cmake | 146 ++ cmake/Modules/FindAsio.cmake | 41 + cmake/Modules/FindGFlags.cmake | 36 + cmake/Modules/FindGlog.cmake | 36 + cmake/Modules/FindHicnBinaryApi.cmake | 31 + cmake/Modules/FindLibEvent.cmake | 55 + cmake/Modules/FindLibhicn.cmake | 49 + cmake/Modules/FindLibmemif.cmake | 47 + cmake/Modules/FindLibparc.cmake | 50 + cmake/Modules/FindLibtransport.cmake | 48 + cmake/Modules/FindLongBow.cmake | 55 + cmake/Modules/FindUncrustify.cmake | 21 + cmake/Modules/FindVpp.cmake | 67 + cmake/Modules/IosMacros.cmake | 24 + cmake/Modules/Packager.cmake | 105 ++ cmake/Modules/detectCacheSize.cmake | 21 + hicn-light/CMakeLists.txt | 74 + hicn-light/README.md | 302 ++++ hicn-light/config/hicn-light.service | 29 + hicn-light/src/CMakeLists.txt | 45 + hicn-light/src/command_line/CMakeLists.txt | 2 + .../src/command_line/controller/CMakeLists.txt | 23 + .../controller/hicnLightControl_main.c | 284 ++++ hicn-light/src/command_line/daemon/CMakeLists.txt | 23 + .../src/command_line/daemon/hicnLightDaemon_main.c | 336 ++++ hicn-light/src/config.h.in | 5 + hicn-light/src/config/CMakeLists.txt | 97 ++ hicn-light/src/config/commandOps.c | 72 + hicn-light/src/config/commandOps.h | 126 ++ hicn-light/src/config/commandParser.c | 216 +++ hicn-light/src/config/commandParser.h | 196 +++ hicn-light/src/config/commandReturn.h | 43 + hicn-light/src/config/configuration.c | 1076 ++++++++++++ hicn-light/src/config/configuration.h | 152 ++ hicn-light/src/config/configurationFile.c | 300 ++++ hicn-light/src/config/configurationFile.h | 93 ++ hicn-light/src/config/configurationListeners.c | 535 ++++++ hicn-light/src/config/configurationListeners.h | 62 + hicn-light/src/config/controlAdd.c | 94 ++ hicn-light/src/config/controlAdd.h | 32 + hicn-light/src/config/controlAddConnection.c | 390 +++++ hicn-light/src/config/controlAddConnection.h | 31 + hicn-light/src/config/controlAddListener.c | 178 ++ hicn-light/src/config/controlAddListener.h | 30 + hicn-light/src/config/controlAddPunting.c | 154 ++ hicn-light/src/config/controlAddPunting.h | 22 + hicn-light/src/config/controlAddRoute.c | 157 ++ hicn-light/src/config/controlAddRoute.h | 30 + hicn-light/src/config/controlCache.c | 91 ++ hicn-light/src/config/controlCache.h | 22 + hicn-light/src/config/controlCacheClear.c | 84 + hicn-light/src/config/controlCacheClear.h | 30 + hicn-light/src/config/controlCacheServe.c | 103 ++ hicn-light/src/config/controlCacheServe.h | 22 + hicn-light/src/config/controlCacheStore.c | 103 ++ hicn-light/src/config/controlCacheStore.h | 22 + hicn-light/src/config/controlList.c | 97 ++ hicn-light/src/config/controlList.h | 30 + hicn-light/src/config/controlListConnections.c | 160 ++ hicn-light/src/config/controlListConnections.h | 30 + hicn-light/src/config/controlListInterfaces.c | 76 + hicn-light/src/config/controlListInterfaces.h | 31 + hicn-light/src/config/controlListListeners.c | 141 ++ hicn-light/src/config/controlListListeners.h | 31 + hicn-light/src/config/controlListRoutes.c | 154 ++ hicn-light/src/config/controlListRoutes.h | 29 + hicn-light/src/config/controlMapMe.c | 94 ++ hicn-light/src/config/controlMapMe.h | 22 + hicn-light/src/config/controlMapMeDiscovery.c | 103 ++ hicn-light/src/config/controlMapMeDiscovery.h | 22 + hicn-light/src/config/controlMapMeEnable.c | 101 ++ hicn-light/src/config/controlMapMeEnable.h | 22 + hicn-light/src/config/controlMapMeRetx.c | 94 ++ hicn-light/src/config/controlMapMeRetx.h | 22 + hicn-light/src/config/controlMapMeTimescale.c | 97 ++ hicn-light/src/config/controlMapMeTimescale.h | 22 + hicn-light/src/config/controlQuit.c | 63 + hicn-light/src/config/controlQuit.h | 29 + hicn-light/src/config/controlRemove.c | 92 ++ hicn-light/src/config/controlRemove.h | 29 + hicn-light/src/config/controlRemoveConnection.c | 115 ++ hicn-light/src/config/controlRemoveConnection.h | 31 + hicn-light/src/config/controlRemovePunting.c | 76 + hicn-light/src/config/controlRemovePunting.h | 27 + hicn-light/src/config/controlRemoveRoute.c | 150 ++ hicn-light/src/config/controlRemoveRoute.h | 31 + hicn-light/src/config/controlRoot.c | 134 ++ hicn-light/src/config/controlRoot.h | 31 + hicn-light/src/config/controlSet.c | 88 + hicn-light/src/config/controlSet.h | 29 + hicn-light/src/config/controlSetDebug.c | 74 + hicn-light/src/config/controlSetDebug.h | 30 + hicn-light/src/config/controlSetStrategy.c | 183 +++ hicn-light/src/config/controlSetStrategy.h | 22 + hicn-light/src/config/controlSetWldr.c | 122 ++ hicn-light/src/config/controlSetWldr.h | 22 + hicn-light/src/config/controlState.c | 237 +++ hicn-light/src/config/controlState.h | 233 +++ hicn-light/src/config/controlUnset.c | 78 + hicn-light/src/config/controlUnset.h | 29 + hicn-light/src/config/controlUnsetDebug.c | 77 + hicn-light/src/config/controlUnsetDebug.h | 30 + hicn-light/src/config/symbolicNameTable.c | 175 ++ hicn-light/src/config/symbolicNameTable.h | 131 ++ hicn-light/src/content_store/CMakeLists.txt | 33 + hicn-light/src/content_store/contentStoreEntry.c | 136 ++ hicn-light/src/content_store/contentStoreEntry.h | 146 ++ .../src/content_store/contentStoreInterface.c | 57 + .../src/content_store/contentStoreInterface.h | 211 +++ hicn-light/src/content_store/contentStoreLRU.c | 455 ++++++ hicn-light/src/content_store/contentStoreLRU.h | 39 + hicn-light/src/content_store/listLRU.c | 133 ++ hicn-light/src/content_store/listLRU.h | 94 ++ hicn-light/src/content_store/listTimeOrdered.c | 94 ++ hicn-light/src/content_store/listTimeOrdered.h | 103 ++ hicn-light/src/core/CMakeLists.txt | 55 + hicn-light/src/core/connection.c | 265 +++ hicn-light/src/core/connection.h | 148 ++ hicn-light/src/core/connectionList.c | 68 + hicn-light/src/core/connectionList.h | 70 + hicn-light/src/core/connectionManager.c | 196 +++ hicn-light/src/core/connectionManager.h | 37 + hicn-light/src/core/connectionTable.c | 224 +++ hicn-light/src/core/connectionTable.h | 99 ++ hicn-light/src/core/dispatcher.c | 435 +++++ hicn-light/src/core/dispatcher.h | 286 ++++ hicn-light/src/core/forwarder.c | 499 ++++++ hicn-light/src/core/forwarder.h | 287 ++++ hicn-light/src/core/logger.c | 173 ++ hicn-light/src/core/logger.h | 168 ++ hicn-light/src/core/mapMe.c | 816 +++++++++ hicn-light/src/core/mapMe.h | 89 + hicn-light/src/core/message.c | 297 ++++ hicn-light/src/core/message.h | 180 ++ hicn-light/src/core/messageHandler.h | 580 +++++++ hicn-light/src/core/messagePacketType.h | 32 + hicn-light/src/core/name.c | 236 +++ hicn-light/src/core/name.h | 105 ++ hicn-light/src/core/nameBitvector.c | 383 +++++ hicn-light/src/core/nameBitvector.h | 63 + hicn-light/src/core/numberSet.c | 203 +++ hicn-light/src/core/numberSet.h | 157 ++ hicn-light/src/core/streamBuffer.c | 142 ++ hicn-light/src/core/streamBuffer.h | 129 ++ hicn-light/src/core/system.h | 60 + hicn-light/src/core/ticks.h | 31 + hicn-light/src/core/wldr.c | 182 +++ hicn-light/src/core/wldr.h | 52 + hicn-light/src/io/CMakeLists.txt | 53 + hicn-light/src/io/addressPair.c | 129 ++ hicn-light/src/io/addressPair.h | 128 ++ hicn-light/src/io/hicnConnection.c | 517 ++++++ hicn-light/src/io/hicnConnection.h | 53 + hicn-light/src/io/hicnListener.c | 725 ++++++++ hicn-light/src/io/hicnListener.h | 42 + hicn-light/src/io/hicnTunnel.c | 106 ++ hicn-light/src/io/hicnTunnel.h | 65 + hicn-light/src/io/ioOperations.c | 63 + hicn-light/src/io/ioOperations.h | 394 +++++ hicn-light/src/io/listener.h | 105 ++ hicn-light/src/io/listenerSet.c | 132 ++ hicn-light/src/io/listenerSet.h | 137 ++ hicn-light/src/io/streamConnection.c | 714 ++++++++ hicn-light/src/io/streamConnection.h | 75 + hicn-light/src/io/tcpListener.c | 233 +++ hicn-light/src/io/tcpListener.h | 37 + hicn-light/src/io/tcpTunnel.c | 43 + hicn-light/src/io/tcpTunnel.h | 42 + hicn-light/src/io/udpConnection.c | 406 +++++ hicn-light/src/io/udpConnection.h | 53 + hicn-light/src/io/udpListener.c | 533 ++++++ hicn-light/src/io/udpListener.h | 32 + hicn-light/src/io/udpTunnel.c | 91 ++ hicn-light/src/io/udpTunnel.h | 42 + hicn-light/src/messenger/CMakeLists.txt | 32 + hicn-light/src/messenger/messenger.c | 171 ++ hicn-light/src/messenger/messenger.h | 69 + hicn-light/src/messenger/messengerRecipient.c | 62 + hicn-light/src/messenger/messengerRecipient.h | 104 ++ hicn-light/src/messenger/missive.c | 54 + hicn-light/src/messenger/missive.h | 89 + hicn-light/src/messenger/missiveDeque.c | 77 + hicn-light/src/messenger/missiveDeque.h | 55 + hicn-light/src/messenger/missiveType.h | 55 + hicn-light/src/platforms/CMakeLists.txt | 31 + hicn-light/src/platforms/README.txt | 17 + hicn-light/src/platforms/android/system.c | 183 +++ hicn-light/src/platforms/darwin/system.c | 146 ++ hicn-light/src/platforms/linux/system.c | 184 +++ hicn-light/src/processor/CMakeLists.txt | 40 + hicn-light/src/processor/fib.c | 448 +++++ hicn-light/src/processor/fib.h | 43 + hicn-light/src/processor/fibEntry.c | 223 +++ hicn-light/src/processor/fibEntry.h | 143 ++ hicn-light/src/processor/fibEntryList.c | 72 + hicn-light/src/processor/fibEntryList.h | 96 ++ hicn-light/src/processor/hashTableFunction.c | 47 + hicn-light/src/processor/hashTableFunction.h | 73 + hicn-light/src/processor/matchingRulesTable.c | 132 ++ hicn-light/src/processor/matchingRulesTable.h | 113 ++ hicn-light/src/processor/messageProcessor.c | 742 +++++++++ hicn-light/src/processor/messageProcessor.h | 166 ++ hicn-light/src/processor/pit.c | 45 + hicn-light/src/processor/pit.h | 114 ++ hicn-light/src/processor/pitEntry.c | 142 ++ hicn-light/src/processor/pitEntry.h | 164 ++ hicn-light/src/processor/pitStandard.c | 304 ++++ hicn-light/src/processor/pitStandard.h | 41 + hicn-light/src/processor/pitVerdict.h | 36 + hicn-light/src/socket/CMakeLists.txt | 31 + hicn-light/src/socket/api.c | 604 +++++++ hicn-light/src/socket/api.h | 216 +++ hicn-light/src/socket/error.c | 7 + hicn-light/src/socket/error.h | 46 + hicn-light/src/socket/ops.h | 54 + hicn-light/src/socket/ops_linux.c | 1723 ++++++++++++++++++++ hicn-light/src/strategies/CMakeLists.txt | 36 + hicn-light/src/strategies/loadBalancer.c | 278 ++++ hicn-light/src/strategies/loadBalancer.h | 26 + hicn-light/src/strategies/loadBalancerWithPD.c | 368 +++++ hicn-light/src/strategies/loadBalancerWithPD.h | 30 + hicn-light/src/strategies/nexthopState.c | 206 +++ hicn-light/src/strategies/nexthopState.h | 94 ++ hicn-light/src/strategies/nexthopStateWithPD.c | 254 +++ hicn-light/src/strategies/nexthopStateWithPD.h | 106 ++ hicn-light/src/strategies/rnd.c | 175 ++ hicn-light/src/strategies/rnd.h | 26 + hicn-light/src/strategies/rndSegment.c | 207 +++ hicn-light/src/strategies/rndSegment.h | 27 + hicn-light/src/strategies/strategyImpl.h | 66 + hicn-light/src/utils/CMakeLists.txt | 36 + hicn-light/src/utils/address.c | 419 +++++ hicn-light/src/utils/address.h | 498 ++++++ hicn-light/src/utils/addressList.c | 133 ++ hicn-light/src/utils/addressList.h | 196 +++ hicn-light/src/utils/commands.h | 282 ++++ hicn-light/src/utils/interface.c | 168 ++ hicn-light/src/utils/interface.h | 208 +++ hicn-light/src/utils/interfaceSet.c | 149 ++ hicn-light/src/utils/interfaceSet.h | 198 +++ hicn-light/src/utils/punting.c | 98 ++ hicn-light/src/utils/punting.h | 72 + hicn-light/src/utils/utils.c | 258 +++ hicn-light/src/utils/utils.h | 82 + hicn-plugin/AUTHORS | 4 + hicn-plugin/CMakeLists.txt | 235 +++ hicn-plugin/README.md | 168 ++ hicn-plugin/src/cache_policies/cs_lru.c | 213 +++ hicn-plugin/src/cache_policies/cs_lru.h | 67 + hicn-plugin/src/cache_policies/cs_policy.h | 81 + hicn-plugin/src/cli.c | 1247 ++++++++++++++ hicn-plugin/src/data_fwd.h | 209 +++ hicn-plugin/src/data_fwd_node.c | 541 ++++++ hicn-plugin/src/data_pcslookup.h | 55 + hicn-plugin/src/data_pcslookup_node.c | 246 +++ hicn-plugin/src/data_push_node.c | 349 ++++ hicn-plugin/src/error.c | 28 + hicn-plugin/src/error.h | 100 ++ hicn-plugin/src/face_db.h | 153 ++ hicn-plugin/src/faces/app/address_mgr.c | 243 +++ hicn-plugin/src/faces/app/address_mgr.h | 76 + hicn-plugin/src/faces/app/face_app_cli.c | 203 +++ hicn-plugin/src/faces/app/face_cons.c | 126 ++ hicn-plugin/src/faces/app/face_cons.h | 75 + hicn-plugin/src/faces/app/face_prod.c | 375 +++++ hicn-plugin/src/faces/app/face_prod.h | 113 ++ hicn-plugin/src/faces/app/face_prod_node.c | 341 ++++ hicn-plugin/src/faces/face.c | 141 ++ hicn-plugin/src/faces/face.h | 240 +++ hicn-plugin/src/faces/face_cli.c | 131 ++ hicn-plugin/src/faces/ip/dpo_ip.c | 187 +++ hicn-plugin/src/faces/ip/dpo_ip.h | 255 +++ hicn-plugin/src/faces/ip/face_ip.c | 326 ++++ hicn-plugin/src/faces/ip/face_ip.h | 241 +++ hicn-plugin/src/faces/ip/face_ip_cli.c | 158 ++ hicn-plugin/src/faces/ip/face_ip_node.c | 761 +++++++++ hicn-plugin/src/faces/ip/face_ip_node.h | 40 + hicn-plugin/src/faces/ip/iface_ip_node.c | 845 ++++++++++ hicn-plugin/src/faces/ip/iface_ip_node.h | 35 + hicn-plugin/src/faces/udp/dpo_udp.c | 158 ++ hicn-plugin/src/faces/udp/dpo_udp.h | 312 ++++ hicn-plugin/src/faces/udp/face_udp.c | 371 +++++ hicn-plugin/src/faces/udp/face_udp.h | 248 +++ hicn-plugin/src/faces/udp/face_udp_cli.c | 164 ++ hicn-plugin/src/faces/udp/face_udp_node.c | 864 ++++++++++ hicn-plugin/src/faces/udp/face_udp_node.h | 35 + hicn-plugin/src/faces/udp/iface_udp_node.c | 894 ++++++++++ hicn-plugin/src/faces/udp/iface_udp_node.h | 36 + hicn-plugin/src/hashtb.c | 1008 ++++++++++++ hicn-plugin/src/hashtb.h | 550 +++++++ hicn-plugin/src/hicn.api | 538 ++++++ hicn-plugin/src/hicn.c | 253 +++ hicn-plugin/src/hicn.h | 86 + hicn-plugin/src/hicn_all_api_h.h | 22 + hicn-plugin/src/hicn_api.c | 570 +++++++ hicn-plugin/src/hicn_api.h | 32 + hicn-plugin/src/hicn_api_test.c | 1046 ++++++++++++ hicn-plugin/src/hicn_msg_enum.h | 36 + hicn-plugin/src/infra.h | 101 ++ hicn-plugin/src/interest_hitcs.h | 55 + hicn-plugin/src/interest_hitcs_node.c | 300 ++++ hicn-plugin/src/interest_hitpit.h | 56 + hicn-plugin/src/interest_hitpit_node.c | 313 ++++ hicn-plugin/src/interest_pcslookup.h | 57 + hicn-plugin/src/interest_pcslookup_node.c | 240 +++ hicn-plugin/src/mapme.h | 307 ++++ hicn-plugin/src/mapme_ack.h | 53 + hicn-plugin/src/mapme_ack_node.c | 224 +++ hicn-plugin/src/mapme_ctrl.h | 92 ++ hicn-plugin/src/mapme_ctrl_node.c | 333 ++++ hicn-plugin/src/mapme_eventmgr.c | 559 +++++++ hicn-plugin/src/mapme_eventmgr.h | 48 + hicn-plugin/src/mgmt.c | 100 ++ hicn-plugin/src/mgmt.h | 132 ++ hicn-plugin/src/params.h | 104 ++ hicn-plugin/src/parser.h | 102 ++ hicn-plugin/src/pcs.c | 53 + hicn-plugin/src/pcs.h | 836 ++++++++++ hicn-plugin/src/pg.c | 1147 +++++++++++++ hicn-plugin/src/pg.h | 56 + hicn-plugin/src/punt.c | 1005 ++++++++++++ hicn-plugin/src/punt.h | 338 ++++ hicn-plugin/src/route.c | 392 +++++ hicn-plugin/src/route.h | 61 + hicn-plugin/src/state.h | 102 ++ hicn-plugin/src/strategies/dpo_mw.c | 305 ++++ hicn-plugin/src/strategies/dpo_mw.h | 131 ++ hicn-plugin/src/strategies/strategy_mw.c | 171 ++ hicn-plugin/src/strategies/strategy_mw.h | 31 + hicn-plugin/src/strategies/strategy_mw_cli.c | 148 ++ hicn-plugin/src/strategy.c | 265 +++ hicn-plugin/src/strategy.h | 82 + hicn-plugin/src/strategy_dpo_ctx.h | 69 + hicn-plugin/src/strategy_dpo_manager.c | 159 ++ hicn-plugin/src/strategy_dpo_manager.h | 186 +++ hicn-plugin/src/utils.h | 66 + hicn-plugin/src/vface_db.h | 155 ++ lib/CMakeLists.txt | 50 + lib/README.md | 114 ++ lib/doc/CMakeLists.txt | 23 + lib/doc/Doxyfile.in | 12 + lib/src/CMakeLists.txt | 81 + lib/src/base.h | 136 ++ lib/src/common.c | 165 ++ lib/src/common.h | 231 +++ lib/src/compat.c | 1177 +++++++++++++ lib/src/compat.h | 462 ++++++ lib/src/error.c | 35 + lib/src/error.h | 58 + lib/src/header.h | 105 ++ lib/src/hicn.h | 79 + lib/src/mapme.c | 224 +++ lib/src/mapme.h | 152 ++ lib/src/name.c | 676 ++++++++ lib/src/name.h | 336 ++++ lib/src/ops.c | 93 ++ lib/src/ops.h | 624 +++++++ lib/src/protocol.h | 51 + lib/src/protocol/ah.c | 211 +++ lib/src/protocol/ah.h | 72 + lib/src/protocol/icmp.c | 229 +++ lib/src/protocol/icmp.h | 68 + lib/src/protocol/icmprd.h | 47 + lib/src/protocol/ipv4.c | 452 +++++ lib/src/protocol/ipv4.h | 91 ++ lib/src/protocol/ipv6.c | 412 +++++ lib/src/protocol/ipv6.h | 67 + lib/src/protocol/tcp.c | 370 +++++ lib/src/protocol/tcp.h | 166 ++ lib/src/protocol/udp.h | 37 + libtransport/AUTHORS | 8 + libtransport/CMakeLists.txt | 122 ++ libtransport/README.md | 126 ++ libtransport/cmake/Modules/Android.cmake | 19 + .../cmake/Modules/DefaultConfiguration.cmake | 48 + libtransport/cmake/Modules/Ios.cmake | 23 + libtransport/cmake/Modules/Packager.cmake | 197 +++ libtransport/cmake/Modules/TestMacros.cmake | 15 + libtransport/src/hicn/transport/CMakeLists.txt | 54 + libtransport/src/hicn/transport/config.h.in | 22 + .../src/hicn/transport/core/CMakeLists.txt | 87 + libtransport/src/hicn/transport/core/connector.cc | 44 + libtransport/src/hicn/transport/core/connector.h | 89 + .../src/hicn/transport/core/content_object.cc | 170 ++ .../src/hicn/transport/core/content_object.h | 69 + libtransport/src/hicn/transport/core/facade.h | 53 + .../src/hicn/transport/core/forwarder_interface.h | 136 ++ .../src/hicn/transport/core/hicn_binary_api.c | 228 +++ .../src/hicn/transport/core/hicn_binary_api.h | 103 ++ .../transport/core/hicn_forwarder_interface.cc | 84 + .../hicn/transport/core/hicn_forwarder_interface.h | 67 + .../src/hicn/transport/core/hicn_memif_api.c | 0 libtransport/src/hicn/transport/core/interest.cc | 149 ++ libtransport/src/hicn/transport/core/interest.h | 66 + .../src/hicn/transport/core/key_locator.cc | 42 + libtransport/src/hicn/transport/core/key_locator.h | 48 + .../src/hicn/transport/core/key_locator_type.h | 28 + libtransport/src/hicn/transport/core/manifest.cc | 33 + libtransport/src/hicn/transport/core/manifest.h | 164 ++ .../src/hicn/transport/core/manifest_format.h | 196 +++ .../hicn/transport/core/manifest_format_fixed.cc | 221 +++ .../hicn/transport/core/manifest_format_fixed.h | 168 ++ .../transport/core/manifest_format_json_jsoncpp.cc | 244 +++ .../transport/core/manifest_format_json_jsoncpp.h | 162 ++ .../manifest_format_json_libparc_deprecated.cc | 298 ++++ .../core/manifest_format_json_libparc_deprecated.h | 152 ++ .../src/hicn/transport/core/manifest_inline.h | 116 ++ .../src/hicn/transport/core/memif_binary_api.c | 218 +++ .../src/hicn/transport/core/memif_binary_api.h | 61 + .../src/hicn/transport/core/memif_connector.cc | 493 ++++++ .../src/hicn/transport/core/memif_connector.h | 162 ++ libtransport/src/hicn/transport/core/name.cc | 236 +++ libtransport/src/hicn/transport/core/name.h | 133 ++ libtransport/src/hicn/transport/core/packet.cc | 614 +++++++ libtransport/src/hicn/transport/core/packet.h | 195 +++ .../src/hicn/transport/core/payload_type.h | 29 + .../src/hicn/transport/core/pending_interest.cc | 49 + .../src/hicn/transport/core/pending_interest.h | 81 + libtransport/src/hicn/transport/core/portal.h | 343 ++++ libtransport/src/hicn/transport/core/prefix.cc | 211 +++ libtransport/src/hicn/transport/core/prefix.h | 68 + .../hicn/transport/core/raw_socket_connector.cc | 214 +++ .../src/hicn/transport/core/raw_socket_connector.h | 85 + .../hicn/transport/core/raw_socket_interface.cc | 57 + .../src/hicn/transport/core/raw_socket_interface.h | 51 + .../src/hicn/transport/core/socket_connector.cc | 237 +++ .../src/hicn/transport/core/socket_connector.h | 89 + .../src/hicn/transport/core/test/CMakeLists.txt | 10 + .../hicn/transport/core/test/test_core_manifest.cc | 296 ++++ .../src/hicn/transport/core/vpp_binary_api.c | 221 +++ .../src/hicn/transport/core/vpp_binary_api.h | 64 + .../hicn/transport/core/vpp_binary_api_internal.h | 61 + .../hicn/transport/core/vpp_forwarder_interface.cc | 184 +++ .../hicn/transport/core/vpp_forwarder_interface.h | 68 + .../src/hicn/transport/errors/CMakeLists.txt | 29 + libtransport/src/hicn/transport/errors/errors.h | 24 + .../errors/invalid_ip_address_exception.h | 31 + .../errors/malformed_ahpacket_exception.h | 31 + .../transport/errors/malformed_name_exception.h | 31 + .../transport/errors/malformed_packet_exception.h | 29 + .../transport/errors/not_implemented_exception.h | 30 + .../hicn/transport/errors/null_pointer_exception.h | 31 + .../src/hicn/transport/errors/runtime_exception.h | 32 + .../hicn/transport/errors/tokenizer_exception.h | 31 + .../src/hicn/transport/http/CMakeLists.txt | 36 + libtransport/src/hicn/transport/http/callbacks.h | 46 + .../src/hicn/transport/http/client_connection.cc | 194 +++ .../src/hicn/transport/http/client_connection.h | 82 + .../src/hicn/transport/http/default_values.h | 32 + libtransport/src/hicn/transport/http/facade.h | 22 + libtransport/src/hicn/transport/http/message.h | 58 + libtransport/src/hicn/transport/http/request.cc | 83 + libtransport/src/hicn/transport/http/request.h | 61 + libtransport/src/hicn/transport/http/response.cc | 134 ++ libtransport/src/hicn/transport/http/response.h | 58 + .../src/hicn/transport/http/server_acceptor.cc | 112 ++ .../src/hicn/transport/http/server_acceptor.h | 62 + .../src/hicn/transport/http/server_publisher.cc | 173 ++ .../src/hicn/transport/http/server_publisher.h | 72 + .../src/hicn/transport/interfaces/CMakeLists.txt | 38 + .../hicn/transport/interfaces/async_transport.h | 640 ++++++++ .../transport/interfaces/full_duplex_socket.cc | 490 ++++++ .../hicn/transport/interfaces/full_duplex_socket.h | 254 +++ .../transport/interfaces/publication_options.h | 34 + .../transport/interfaces/rtc_socket_consumer.cc | 35 + .../transport/interfaces/rtc_socket_consumer.h | 35 + .../transport/interfaces/rtc_socket_producer.cc | 157 ++ .../transport/interfaces/rtc_socket_producer.h | 60 + .../src/hicn/transport/interfaces/socket.h | 270 +++ .../hicn/transport/interfaces/socket_consumer.cc | 735 +++++++++ .../hicn/transport/interfaces/socket_consumer.h | 259 +++ .../interfaces/socket_options_default_values.h | 68 + .../transport/interfaces/socket_options_keys.h | 108 ++ .../hicn/transport/interfaces/socket_producer.cc | 948 +++++++++++ .../hicn/transport/interfaces/socket_producer.h | 269 +++ .../src/hicn/transport/portability/CMakeLists.txt | 26 + .../src/hicn/transport/portability/c_portability.h | 36 + .../src/hicn/transport/portability/portability.h | 46 + .../src/hicn/transport/protocols/CMakeLists.txt | 46 + libtransport/src/hicn/transport/protocols/cbr.cc | 47 + libtransport/src/hicn/transport/protocols/cbr.h | 48 + .../src/hicn/transport/protocols/consumer.conf | 21 + .../hicn/transport/protocols/download_observer.h | 32 + .../src/hicn/transport/protocols/protocol.cc | 45 + .../src/hicn/transport/protocols/protocol.h | 79 + libtransport/src/hicn/transport/protocols/raaqm.cc | 416 +++++ libtransport/src/hicn/transport/protocols/raaqm.h | 94 ++ .../hicn/transport/protocols/raaqm_data_path.cc | 158 ++ .../src/hicn/transport/protocols/raaqm_data_path.h | 230 +++ .../hicn/transport/protocols/rate_estimation.cc | 353 ++++ .../src/hicn/transport/protocols/rate_estimation.h | 175 ++ libtransport/src/hicn/transport/protocols/rtc.cc | 813 +++++++++ libtransport/src/hicn/transport/protocols/rtc.h | 210 +++ .../src/hicn/transport/protocols/rtc_data_path.cc | 85 + .../src/hicn/transport/protocols/rtc_data_path.h | 62 + .../hicn/transport/protocols/test/CMakeLists.txt | 10 + .../protocols/test/test_transport_producer.cc | 80 + libtransport/src/hicn/transport/protocols/vegas.cc | 630 +++++++ libtransport/src/hicn/transport/protocols/vegas.h | 161 ++ .../transport/protocols/vegas_rto_estimator.cc | 57 + .../hicn/transport/protocols/vegas_rto_estimator.h | 48 + .../src/hicn/transport/utils/CMakeLists.txt | 76 + libtransport/src/hicn/transport/utils/array.h | 62 + .../src/hicn/transport/utils/branch_prediction.h | 22 + .../src/hicn/transport/utils/content_store.cc | 109 ++ .../src/hicn/transport/utils/content_store.h | 75 + .../src/hicn/transport/utils/conversions.h | 37 + .../src/hicn/transport/utils/crypto_hash.h | 115 ++ .../src/hicn/transport/utils/crypto_hash_type.h | 31 + .../src/hicn/transport/utils/crypto_hasher.h | 68 + .../src/hicn/transport/utils/crypto_suite.h | 35 + .../src/hicn/transport/utils/daemonizator.cc | 73 + .../src/hicn/transport/utils/daemonizator.h | 25 + .../src/hicn/transport/utils/deadline_timer.h | 114 ++ libtransport/src/hicn/transport/utils/endianess.h | 136 ++ .../hicn/transport/utils/epoll_event_reactor.cc | 183 +++ .../src/hicn/transport/utils/epoll_event_reactor.h | 65 + .../src/hicn/transport/utils/event_reactor.h | 37 + .../src/hicn/transport/utils/event_thread.h | 101 ++ .../src/hicn/transport/utils/fd_deadline_timer.h | 127 ++ libtransport/src/hicn/transport/utils/hash.h | 101 ++ libtransport/src/hicn/transport/utils/identity.cc | 130 ++ libtransport/src/hicn/transport/utils/identity.h | 64 + libtransport/src/hicn/transport/utils/key_id.h | 25 + libtransport/src/hicn/transport/utils/linux.h | 64 + libtransport/src/hicn/transport/utils/literals.h | 55 + libtransport/src/hicn/transport/utils/log.cc | 1405 ++++++++++++++++ libtransport/src/hicn/transport/utils/log.h | 1057 ++++++++++++ libtransport/src/hicn/transport/utils/membuf.cc | 864 ++++++++++ libtransport/src/hicn/transport/utils/membuf.h | 916 +++++++++++ libtransport/src/hicn/transport/utils/min_filter.h | 56 + .../src/hicn/transport/utils/object_pool.h | 76 + .../src/hicn/transport/utils/ring_buffer.h | 129 ++ .../src/hicn/transport/utils/sharable_vector.h | 30 + libtransport/src/hicn/transport/utils/signer.cc | 173 ++ libtransport/src/hicn/transport/utils/signer.h | 69 + libtransport/src/hicn/transport/utils/socket.h | 267 +++ libtransport/src/hicn/transport/utils/spinlock.h | 53 + .../src/hicn/transport/utils/stream_buffer.h | 31 + .../src/hicn/transport/utils/string_tokenizer.cc | 47 + .../src/hicn/transport/utils/string_tokenizer.h | 35 + libtransport/src/hicn/transport/utils/test.h | 46 + libtransport/src/hicn/transport/utils/uri.cc | 122 ++ libtransport/src/hicn/transport/utils/uri.h | 47 + libtransport/src/hicn/transport/utils/verifier.cc | 193 +++ libtransport/src/hicn/transport/utils/verifier.h | 85 + utils/CMakeLists.txt | 54 + utils/src/hiperf.cc | 724 ++++++++ utils/src/ping_client.cc | 428 +++++ utils/src/ping_server.cc | 300 ++++ 556 files changed, 94971 insertions(+) create mode 100644 .gitreview create mode 100755 AUTHORS create mode 100755 CMakeLists.txt create mode 100755 LICENSE create mode 100755 README.md create mode 100755 apps/README.md create mode 100755 cmake/Modules/BuildMacros.cmake create mode 100755 cmake/Modules/FindAsio.cmake create mode 100755 cmake/Modules/FindGFlags.cmake create mode 100755 cmake/Modules/FindGlog.cmake create mode 100755 cmake/Modules/FindHicnBinaryApi.cmake create mode 100755 cmake/Modules/FindLibEvent.cmake create mode 100755 cmake/Modules/FindLibhicn.cmake create mode 100755 cmake/Modules/FindLibmemif.cmake create mode 100755 cmake/Modules/FindLibparc.cmake create mode 100755 cmake/Modules/FindLibtransport.cmake create mode 100755 cmake/Modules/FindLongBow.cmake create mode 100755 cmake/Modules/FindUncrustify.cmake create mode 100755 cmake/Modules/FindVpp.cmake create mode 100755 cmake/Modules/IosMacros.cmake create mode 100755 cmake/Modules/Packager.cmake create mode 100755 cmake/Modules/detectCacheSize.cmake create mode 100755 hicn-light/CMakeLists.txt create mode 100755 hicn-light/README.md create mode 100755 hicn-light/config/hicn-light.service create mode 100755 hicn-light/src/CMakeLists.txt create mode 100755 hicn-light/src/command_line/CMakeLists.txt create mode 100755 hicn-light/src/command_line/controller/CMakeLists.txt create mode 100755 hicn-light/src/command_line/controller/hicnLightControl_main.c create mode 100755 hicn-light/src/command_line/daemon/CMakeLists.txt create mode 100755 hicn-light/src/command_line/daemon/hicnLightDaemon_main.c create mode 100755 hicn-light/src/config.h.in create mode 100755 hicn-light/src/config/CMakeLists.txt create mode 100755 hicn-light/src/config/commandOps.c create mode 100755 hicn-light/src/config/commandOps.h create mode 100755 hicn-light/src/config/commandParser.c create mode 100755 hicn-light/src/config/commandParser.h create mode 100755 hicn-light/src/config/commandReturn.h create mode 100755 hicn-light/src/config/configuration.c create mode 100755 hicn-light/src/config/configuration.h create mode 100755 hicn-light/src/config/configurationFile.c create mode 100755 hicn-light/src/config/configurationFile.h create mode 100755 hicn-light/src/config/configurationListeners.c create mode 100755 hicn-light/src/config/configurationListeners.h create mode 100755 hicn-light/src/config/controlAdd.c create mode 100755 hicn-light/src/config/controlAdd.h create mode 100755 hicn-light/src/config/controlAddConnection.c create mode 100755 hicn-light/src/config/controlAddConnection.h create mode 100755 hicn-light/src/config/controlAddListener.c create mode 100755 hicn-light/src/config/controlAddListener.h create mode 100755 hicn-light/src/config/controlAddPunting.c create mode 100755 hicn-light/src/config/controlAddPunting.h create mode 100755 hicn-light/src/config/controlAddRoute.c create mode 100755 hicn-light/src/config/controlAddRoute.h create mode 100755 hicn-light/src/config/controlCache.c create mode 100755 hicn-light/src/config/controlCache.h create mode 100755 hicn-light/src/config/controlCacheClear.c create mode 100755 hicn-light/src/config/controlCacheClear.h create mode 100755 hicn-light/src/config/controlCacheServe.c create mode 100755 hicn-light/src/config/controlCacheServe.h create mode 100755 hicn-light/src/config/controlCacheStore.c create mode 100755 hicn-light/src/config/controlCacheStore.h create mode 100755 hicn-light/src/config/controlList.c create mode 100755 hicn-light/src/config/controlList.h create mode 100755 hicn-light/src/config/controlListConnections.c create mode 100755 hicn-light/src/config/controlListConnections.h create mode 100755 hicn-light/src/config/controlListInterfaces.c create mode 100755 hicn-light/src/config/controlListInterfaces.h create mode 100755 hicn-light/src/config/controlListListeners.c create mode 100755 hicn-light/src/config/controlListListeners.h create mode 100755 hicn-light/src/config/controlListRoutes.c create mode 100755 hicn-light/src/config/controlListRoutes.h create mode 100755 hicn-light/src/config/controlMapMe.c create mode 100755 hicn-light/src/config/controlMapMe.h create mode 100755 hicn-light/src/config/controlMapMeDiscovery.c create mode 100755 hicn-light/src/config/controlMapMeDiscovery.h create mode 100755 hicn-light/src/config/controlMapMeEnable.c create mode 100755 hicn-light/src/config/controlMapMeEnable.h create mode 100755 hicn-light/src/config/controlMapMeRetx.c create mode 100755 hicn-light/src/config/controlMapMeRetx.h create mode 100755 hicn-light/src/config/controlMapMeTimescale.c create mode 100755 hicn-light/src/config/controlMapMeTimescale.h create mode 100755 hicn-light/src/config/controlQuit.c create mode 100755 hicn-light/src/config/controlQuit.h create mode 100755 hicn-light/src/config/controlRemove.c create mode 100755 hicn-light/src/config/controlRemove.h create mode 100755 hicn-light/src/config/controlRemoveConnection.c create mode 100755 hicn-light/src/config/controlRemoveConnection.h create mode 100755 hicn-light/src/config/controlRemovePunting.c create mode 100755 hicn-light/src/config/controlRemovePunting.h create mode 100755 hicn-light/src/config/controlRemoveRoute.c create mode 100755 hicn-light/src/config/controlRemoveRoute.h create mode 100755 hicn-light/src/config/controlRoot.c create mode 100755 hicn-light/src/config/controlRoot.h create mode 100755 hicn-light/src/config/controlSet.c create mode 100755 hicn-light/src/config/controlSet.h create mode 100755 hicn-light/src/config/controlSetDebug.c create mode 100755 hicn-light/src/config/controlSetDebug.h create mode 100755 hicn-light/src/config/controlSetStrategy.c create mode 100755 hicn-light/src/config/controlSetStrategy.h create mode 100755 hicn-light/src/config/controlSetWldr.c create mode 100755 hicn-light/src/config/controlSetWldr.h create mode 100755 hicn-light/src/config/controlState.c create mode 100755 hicn-light/src/config/controlState.h create mode 100755 hicn-light/src/config/controlUnset.c create mode 100755 hicn-light/src/config/controlUnset.h create mode 100755 hicn-light/src/config/controlUnsetDebug.c create mode 100755 hicn-light/src/config/controlUnsetDebug.h create mode 100755 hicn-light/src/config/symbolicNameTable.c create mode 100755 hicn-light/src/config/symbolicNameTable.h create mode 100755 hicn-light/src/content_store/CMakeLists.txt create mode 100755 hicn-light/src/content_store/contentStoreEntry.c create mode 100755 hicn-light/src/content_store/contentStoreEntry.h create mode 100755 hicn-light/src/content_store/contentStoreInterface.c create mode 100755 hicn-light/src/content_store/contentStoreInterface.h create mode 100755 hicn-light/src/content_store/contentStoreLRU.c create mode 100755 hicn-light/src/content_store/contentStoreLRU.h create mode 100755 hicn-light/src/content_store/listLRU.c create mode 100755 hicn-light/src/content_store/listLRU.h create mode 100755 hicn-light/src/content_store/listTimeOrdered.c create mode 100755 hicn-light/src/content_store/listTimeOrdered.h create mode 100755 hicn-light/src/core/CMakeLists.txt create mode 100755 hicn-light/src/core/connection.c create mode 100755 hicn-light/src/core/connection.h create mode 100755 hicn-light/src/core/connectionList.c create mode 100755 hicn-light/src/core/connectionList.h create mode 100755 hicn-light/src/core/connectionManager.c create mode 100755 hicn-light/src/core/connectionManager.h create mode 100755 hicn-light/src/core/connectionTable.c create mode 100755 hicn-light/src/core/connectionTable.h create mode 100755 hicn-light/src/core/dispatcher.c create mode 100755 hicn-light/src/core/dispatcher.h create mode 100755 hicn-light/src/core/forwarder.c create mode 100755 hicn-light/src/core/forwarder.h create mode 100755 hicn-light/src/core/logger.c create mode 100755 hicn-light/src/core/logger.h create mode 100755 hicn-light/src/core/mapMe.c create mode 100755 hicn-light/src/core/mapMe.h create mode 100755 hicn-light/src/core/message.c create mode 100755 hicn-light/src/core/message.h create mode 100755 hicn-light/src/core/messageHandler.h create mode 100755 hicn-light/src/core/messagePacketType.h create mode 100755 hicn-light/src/core/name.c create mode 100755 hicn-light/src/core/name.h create mode 100755 hicn-light/src/core/nameBitvector.c create mode 100755 hicn-light/src/core/nameBitvector.h create mode 100755 hicn-light/src/core/numberSet.c create mode 100755 hicn-light/src/core/numberSet.h create mode 100755 hicn-light/src/core/streamBuffer.c create mode 100755 hicn-light/src/core/streamBuffer.h create mode 100755 hicn-light/src/core/system.h create mode 100755 hicn-light/src/core/ticks.h create mode 100755 hicn-light/src/core/wldr.c create mode 100755 hicn-light/src/core/wldr.h create mode 100755 hicn-light/src/io/CMakeLists.txt create mode 100755 hicn-light/src/io/addressPair.c create mode 100755 hicn-light/src/io/addressPair.h create mode 100755 hicn-light/src/io/hicnConnection.c create mode 100755 hicn-light/src/io/hicnConnection.h create mode 100755 hicn-light/src/io/hicnListener.c create mode 100755 hicn-light/src/io/hicnListener.h create mode 100755 hicn-light/src/io/hicnTunnel.c create mode 100755 hicn-light/src/io/hicnTunnel.h create mode 100755 hicn-light/src/io/ioOperations.c create mode 100755 hicn-light/src/io/ioOperations.h create mode 100755 hicn-light/src/io/listener.h create mode 100755 hicn-light/src/io/listenerSet.c create mode 100755 hicn-light/src/io/listenerSet.h create mode 100755 hicn-light/src/io/streamConnection.c create mode 100755 hicn-light/src/io/streamConnection.h create mode 100755 hicn-light/src/io/tcpListener.c create mode 100755 hicn-light/src/io/tcpListener.h create mode 100755 hicn-light/src/io/tcpTunnel.c create mode 100755 hicn-light/src/io/tcpTunnel.h create mode 100755 hicn-light/src/io/udpConnection.c create mode 100755 hicn-light/src/io/udpConnection.h create mode 100755 hicn-light/src/io/udpListener.c create mode 100755 hicn-light/src/io/udpListener.h create mode 100755 hicn-light/src/io/udpTunnel.c create mode 100755 hicn-light/src/io/udpTunnel.h create mode 100755 hicn-light/src/messenger/CMakeLists.txt create mode 100755 hicn-light/src/messenger/messenger.c create mode 100755 hicn-light/src/messenger/messenger.h create mode 100755 hicn-light/src/messenger/messengerRecipient.c create mode 100755 hicn-light/src/messenger/messengerRecipient.h create mode 100755 hicn-light/src/messenger/missive.c create mode 100755 hicn-light/src/messenger/missive.h create mode 100755 hicn-light/src/messenger/missiveDeque.c create mode 100755 hicn-light/src/messenger/missiveDeque.h create mode 100755 hicn-light/src/messenger/missiveType.h create mode 100755 hicn-light/src/platforms/CMakeLists.txt create mode 100755 hicn-light/src/platforms/README.txt create mode 100755 hicn-light/src/platforms/android/system.c create mode 100755 hicn-light/src/platforms/darwin/system.c create mode 100755 hicn-light/src/platforms/linux/system.c create mode 100755 hicn-light/src/processor/CMakeLists.txt create mode 100755 hicn-light/src/processor/fib.c create mode 100755 hicn-light/src/processor/fib.h create mode 100755 hicn-light/src/processor/fibEntry.c create mode 100755 hicn-light/src/processor/fibEntry.h create mode 100755 hicn-light/src/processor/fibEntryList.c create mode 100755 hicn-light/src/processor/fibEntryList.h create mode 100755 hicn-light/src/processor/hashTableFunction.c create mode 100755 hicn-light/src/processor/hashTableFunction.h create mode 100755 hicn-light/src/processor/matchingRulesTable.c create mode 100755 hicn-light/src/processor/matchingRulesTable.h create mode 100755 hicn-light/src/processor/messageProcessor.c create mode 100755 hicn-light/src/processor/messageProcessor.h create mode 100755 hicn-light/src/processor/pit.c create mode 100755 hicn-light/src/processor/pit.h create mode 100755 hicn-light/src/processor/pitEntry.c create mode 100755 hicn-light/src/processor/pitEntry.h create mode 100755 hicn-light/src/processor/pitStandard.c create mode 100755 hicn-light/src/processor/pitStandard.h create mode 100755 hicn-light/src/processor/pitVerdict.h create mode 100755 hicn-light/src/socket/CMakeLists.txt create mode 100755 hicn-light/src/socket/api.c create mode 100755 hicn-light/src/socket/api.h create mode 100755 hicn-light/src/socket/error.c create mode 100755 hicn-light/src/socket/error.h create mode 100755 hicn-light/src/socket/ops.h create mode 100755 hicn-light/src/socket/ops_linux.c create mode 100755 hicn-light/src/strategies/CMakeLists.txt create mode 100755 hicn-light/src/strategies/loadBalancer.c create mode 100755 hicn-light/src/strategies/loadBalancer.h create mode 100755 hicn-light/src/strategies/loadBalancerWithPD.c create mode 100755 hicn-light/src/strategies/loadBalancerWithPD.h create mode 100755 hicn-light/src/strategies/nexthopState.c create mode 100755 hicn-light/src/strategies/nexthopState.h create mode 100755 hicn-light/src/strategies/nexthopStateWithPD.c create mode 100755 hicn-light/src/strategies/nexthopStateWithPD.h create mode 100755 hicn-light/src/strategies/rnd.c create mode 100755 hicn-light/src/strategies/rnd.h create mode 100755 hicn-light/src/strategies/rndSegment.c create mode 100755 hicn-light/src/strategies/rndSegment.h create mode 100755 hicn-light/src/strategies/strategyImpl.h create mode 100755 hicn-light/src/utils/CMakeLists.txt create mode 100755 hicn-light/src/utils/address.c create mode 100755 hicn-light/src/utils/address.h create mode 100755 hicn-light/src/utils/addressList.c create mode 100755 hicn-light/src/utils/addressList.h create mode 100755 hicn-light/src/utils/commands.h create mode 100755 hicn-light/src/utils/interface.c create mode 100755 hicn-light/src/utils/interface.h create mode 100755 hicn-light/src/utils/interfaceSet.c create mode 100755 hicn-light/src/utils/interfaceSet.h create mode 100755 hicn-light/src/utils/punting.c create mode 100755 hicn-light/src/utils/punting.h create mode 100755 hicn-light/src/utils/utils.c create mode 100755 hicn-light/src/utils/utils.h create mode 100755 hicn-plugin/AUTHORS create mode 100755 hicn-plugin/CMakeLists.txt create mode 100755 hicn-plugin/README.md create mode 100755 hicn-plugin/src/cache_policies/cs_lru.c create mode 100755 hicn-plugin/src/cache_policies/cs_lru.h create mode 100755 hicn-plugin/src/cache_policies/cs_policy.h create mode 100755 hicn-plugin/src/cli.c create mode 100755 hicn-plugin/src/data_fwd.h create mode 100755 hicn-plugin/src/data_fwd_node.c create mode 100755 hicn-plugin/src/data_pcslookup.h create mode 100755 hicn-plugin/src/data_pcslookup_node.c create mode 100755 hicn-plugin/src/data_push_node.c create mode 100755 hicn-plugin/src/error.c create mode 100755 hicn-plugin/src/error.h create mode 100755 hicn-plugin/src/face_db.h create mode 100755 hicn-plugin/src/faces/app/address_mgr.c create mode 100755 hicn-plugin/src/faces/app/address_mgr.h create mode 100755 hicn-plugin/src/faces/app/face_app_cli.c create mode 100755 hicn-plugin/src/faces/app/face_cons.c create mode 100755 hicn-plugin/src/faces/app/face_cons.h create mode 100755 hicn-plugin/src/faces/app/face_prod.c create mode 100755 hicn-plugin/src/faces/app/face_prod.h create mode 100755 hicn-plugin/src/faces/app/face_prod_node.c create mode 100755 hicn-plugin/src/faces/face.c create mode 100755 hicn-plugin/src/faces/face.h create mode 100755 hicn-plugin/src/faces/face_cli.c create mode 100755 hicn-plugin/src/faces/ip/dpo_ip.c create mode 100755 hicn-plugin/src/faces/ip/dpo_ip.h create mode 100755 hicn-plugin/src/faces/ip/face_ip.c create mode 100755 hicn-plugin/src/faces/ip/face_ip.h create mode 100755 hicn-plugin/src/faces/ip/face_ip_cli.c create mode 100755 hicn-plugin/src/faces/ip/face_ip_node.c create mode 100755 hicn-plugin/src/faces/ip/face_ip_node.h create mode 100755 hicn-plugin/src/faces/ip/iface_ip_node.c create mode 100755 hicn-plugin/src/faces/ip/iface_ip_node.h create mode 100755 hicn-plugin/src/faces/udp/dpo_udp.c create mode 100755 hicn-plugin/src/faces/udp/dpo_udp.h create mode 100755 hicn-plugin/src/faces/udp/face_udp.c create mode 100755 hicn-plugin/src/faces/udp/face_udp.h create mode 100755 hicn-plugin/src/faces/udp/face_udp_cli.c create mode 100755 hicn-plugin/src/faces/udp/face_udp_node.c create mode 100755 hicn-plugin/src/faces/udp/face_udp_node.h create mode 100755 hicn-plugin/src/faces/udp/iface_udp_node.c create mode 100755 hicn-plugin/src/faces/udp/iface_udp_node.h create mode 100755 hicn-plugin/src/hashtb.c create mode 100755 hicn-plugin/src/hashtb.h create mode 100755 hicn-plugin/src/hicn.api create mode 100755 hicn-plugin/src/hicn.c create mode 100755 hicn-plugin/src/hicn.h create mode 100755 hicn-plugin/src/hicn_all_api_h.h create mode 100755 hicn-plugin/src/hicn_api.c create mode 100755 hicn-plugin/src/hicn_api.h create mode 100755 hicn-plugin/src/hicn_api_test.c create mode 100755 hicn-plugin/src/hicn_msg_enum.h create mode 100755 hicn-plugin/src/infra.h create mode 100755 hicn-plugin/src/interest_hitcs.h create mode 100755 hicn-plugin/src/interest_hitcs_node.c create mode 100755 hicn-plugin/src/interest_hitpit.h create mode 100755 hicn-plugin/src/interest_hitpit_node.c create mode 100755 hicn-plugin/src/interest_pcslookup.h create mode 100755 hicn-plugin/src/interest_pcslookup_node.c create mode 100755 hicn-plugin/src/mapme.h create mode 100755 hicn-plugin/src/mapme_ack.h create mode 100755 hicn-plugin/src/mapme_ack_node.c create mode 100755 hicn-plugin/src/mapme_ctrl.h create mode 100755 hicn-plugin/src/mapme_ctrl_node.c create mode 100755 hicn-plugin/src/mapme_eventmgr.c create mode 100755 hicn-plugin/src/mapme_eventmgr.h create mode 100755 hicn-plugin/src/mgmt.c create mode 100755 hicn-plugin/src/mgmt.h create mode 100755 hicn-plugin/src/params.h create mode 100755 hicn-plugin/src/parser.h create mode 100755 hicn-plugin/src/pcs.c create mode 100755 hicn-plugin/src/pcs.h create mode 100755 hicn-plugin/src/pg.c create mode 100755 hicn-plugin/src/pg.h create mode 100755 hicn-plugin/src/punt.c create mode 100755 hicn-plugin/src/punt.h create mode 100755 hicn-plugin/src/route.c create mode 100755 hicn-plugin/src/route.h create mode 100755 hicn-plugin/src/state.h create mode 100755 hicn-plugin/src/strategies/dpo_mw.c create mode 100755 hicn-plugin/src/strategies/dpo_mw.h create mode 100755 hicn-plugin/src/strategies/strategy_mw.c create mode 100755 hicn-plugin/src/strategies/strategy_mw.h create mode 100755 hicn-plugin/src/strategies/strategy_mw_cli.c create mode 100755 hicn-plugin/src/strategy.c create mode 100755 hicn-plugin/src/strategy.h create mode 100755 hicn-plugin/src/strategy_dpo_ctx.h create mode 100755 hicn-plugin/src/strategy_dpo_manager.c create mode 100755 hicn-plugin/src/strategy_dpo_manager.h create mode 100755 hicn-plugin/src/utils.h create mode 100755 hicn-plugin/src/vface_db.h create mode 100755 lib/CMakeLists.txt create mode 100755 lib/README.md create mode 100755 lib/doc/CMakeLists.txt create mode 100755 lib/doc/Doxyfile.in create mode 100755 lib/src/CMakeLists.txt create mode 100755 lib/src/base.h create mode 100755 lib/src/common.c create mode 100755 lib/src/common.h create mode 100755 lib/src/compat.c create mode 100755 lib/src/compat.h create mode 100755 lib/src/error.c create mode 100755 lib/src/error.h create mode 100755 lib/src/header.h create mode 100755 lib/src/hicn.h create mode 100755 lib/src/mapme.c create mode 100755 lib/src/mapme.h create mode 100755 lib/src/name.c create mode 100755 lib/src/name.h create mode 100755 lib/src/ops.c create mode 100755 lib/src/ops.h create mode 100755 lib/src/protocol.h create mode 100755 lib/src/protocol/ah.c create mode 100755 lib/src/protocol/ah.h create mode 100755 lib/src/protocol/icmp.c create mode 100755 lib/src/protocol/icmp.h create mode 100755 lib/src/protocol/icmprd.h create mode 100755 lib/src/protocol/ipv4.c create mode 100755 lib/src/protocol/ipv4.h create mode 100755 lib/src/protocol/ipv6.c create mode 100755 lib/src/protocol/ipv6.h create mode 100755 lib/src/protocol/tcp.c create mode 100755 lib/src/protocol/tcp.h create mode 100755 lib/src/protocol/udp.h create mode 100755 libtransport/AUTHORS create mode 100755 libtransport/CMakeLists.txt create mode 100755 libtransport/README.md create mode 100755 libtransport/cmake/Modules/Android.cmake create mode 100755 libtransport/cmake/Modules/DefaultConfiguration.cmake create mode 100755 libtransport/cmake/Modules/Ios.cmake create mode 100755 libtransport/cmake/Modules/Packager.cmake create mode 100755 libtransport/cmake/Modules/TestMacros.cmake create mode 100755 libtransport/src/hicn/transport/CMakeLists.txt create mode 100755 libtransport/src/hicn/transport/config.h.in create mode 100755 libtransport/src/hicn/transport/core/CMakeLists.txt create mode 100755 libtransport/src/hicn/transport/core/connector.cc create mode 100755 libtransport/src/hicn/transport/core/connector.h create mode 100755 libtransport/src/hicn/transport/core/content_object.cc create mode 100755 libtransport/src/hicn/transport/core/content_object.h create mode 100755 libtransport/src/hicn/transport/core/facade.h create mode 100755 libtransport/src/hicn/transport/core/forwarder_interface.h create mode 100755 libtransport/src/hicn/transport/core/hicn_binary_api.c create mode 100755 libtransport/src/hicn/transport/core/hicn_binary_api.h create mode 100755 libtransport/src/hicn/transport/core/hicn_forwarder_interface.cc create mode 100755 libtransport/src/hicn/transport/core/hicn_forwarder_interface.h create mode 100755 libtransport/src/hicn/transport/core/hicn_memif_api.c create mode 100755 libtransport/src/hicn/transport/core/interest.cc create mode 100755 libtransport/src/hicn/transport/core/interest.h create mode 100755 libtransport/src/hicn/transport/core/key_locator.cc create mode 100755 libtransport/src/hicn/transport/core/key_locator.h create mode 100755 libtransport/src/hicn/transport/core/key_locator_type.h create mode 100755 libtransport/src/hicn/transport/core/manifest.cc create mode 100755 libtransport/src/hicn/transport/core/manifest.h create mode 100755 libtransport/src/hicn/transport/core/manifest_format.h create mode 100755 libtransport/src/hicn/transport/core/manifest_format_fixed.cc create mode 100755 libtransport/src/hicn/transport/core/manifest_format_fixed.h create mode 100755 libtransport/src/hicn/transport/core/manifest_format_json_jsoncpp.cc create mode 100755 libtransport/src/hicn/transport/core/manifest_format_json_jsoncpp.h create mode 100755 libtransport/src/hicn/transport/core/manifest_format_json_libparc_deprecated.cc create mode 100755 libtransport/src/hicn/transport/core/manifest_format_json_libparc_deprecated.h create mode 100755 libtransport/src/hicn/transport/core/manifest_inline.h create mode 100755 libtransport/src/hicn/transport/core/memif_binary_api.c create mode 100755 libtransport/src/hicn/transport/core/memif_binary_api.h create mode 100755 libtransport/src/hicn/transport/core/memif_connector.cc create mode 100755 libtransport/src/hicn/transport/core/memif_connector.h create mode 100755 libtransport/src/hicn/transport/core/name.cc create mode 100755 libtransport/src/hicn/transport/core/name.h create mode 100755 libtransport/src/hicn/transport/core/packet.cc create mode 100755 libtransport/src/hicn/transport/core/packet.h create mode 100755 libtransport/src/hicn/transport/core/payload_type.h create mode 100755 libtransport/src/hicn/transport/core/pending_interest.cc create mode 100755 libtransport/src/hicn/transport/core/pending_interest.h create mode 100755 libtransport/src/hicn/transport/core/portal.h create mode 100755 libtransport/src/hicn/transport/core/prefix.cc create mode 100755 libtransport/src/hicn/transport/core/prefix.h create mode 100755 libtransport/src/hicn/transport/core/raw_socket_connector.cc create mode 100755 libtransport/src/hicn/transport/core/raw_socket_connector.h create mode 100755 libtransport/src/hicn/transport/core/raw_socket_interface.cc create mode 100755 libtransport/src/hicn/transport/core/raw_socket_interface.h create mode 100755 libtransport/src/hicn/transport/core/socket_connector.cc create mode 100755 libtransport/src/hicn/transport/core/socket_connector.h create mode 100755 libtransport/src/hicn/transport/core/test/CMakeLists.txt create mode 100755 libtransport/src/hicn/transport/core/test/test_core_manifest.cc create mode 100755 libtransport/src/hicn/transport/core/vpp_binary_api.c create mode 100755 libtransport/src/hicn/transport/core/vpp_binary_api.h create mode 100755 libtransport/src/hicn/transport/core/vpp_binary_api_internal.h create mode 100755 libtransport/src/hicn/transport/core/vpp_forwarder_interface.cc create mode 100755 libtransport/src/hicn/transport/core/vpp_forwarder_interface.h create mode 100755 libtransport/src/hicn/transport/errors/CMakeLists.txt create mode 100755 libtransport/src/hicn/transport/errors/errors.h create mode 100755 libtransport/src/hicn/transport/errors/invalid_ip_address_exception.h create mode 100755 libtransport/src/hicn/transport/errors/malformed_ahpacket_exception.h create mode 100755 libtransport/src/hicn/transport/errors/malformed_name_exception.h create mode 100755 libtransport/src/hicn/transport/errors/malformed_packet_exception.h create mode 100755 libtransport/src/hicn/transport/errors/not_implemented_exception.h create mode 100755 libtransport/src/hicn/transport/errors/null_pointer_exception.h create mode 100755 libtransport/src/hicn/transport/errors/runtime_exception.h create mode 100755 libtransport/src/hicn/transport/errors/tokenizer_exception.h create mode 100755 libtransport/src/hicn/transport/http/CMakeLists.txt create mode 100755 libtransport/src/hicn/transport/http/callbacks.h create mode 100755 libtransport/src/hicn/transport/http/client_connection.cc create mode 100755 libtransport/src/hicn/transport/http/client_connection.h create mode 100755 libtransport/src/hicn/transport/http/default_values.h create mode 100755 libtransport/src/hicn/transport/http/facade.h create mode 100755 libtransport/src/hicn/transport/http/message.h create mode 100755 libtransport/src/hicn/transport/http/request.cc create mode 100755 libtransport/src/hicn/transport/http/request.h create mode 100755 libtransport/src/hicn/transport/http/response.cc create mode 100755 libtransport/src/hicn/transport/http/response.h create mode 100755 libtransport/src/hicn/transport/http/server_acceptor.cc create mode 100755 libtransport/src/hicn/transport/http/server_acceptor.h create mode 100755 libtransport/src/hicn/transport/http/server_publisher.cc create mode 100755 libtransport/src/hicn/transport/http/server_publisher.h create mode 100755 libtransport/src/hicn/transport/interfaces/CMakeLists.txt create mode 100755 libtransport/src/hicn/transport/interfaces/async_transport.h create mode 100755 libtransport/src/hicn/transport/interfaces/full_duplex_socket.cc create mode 100755 libtransport/src/hicn/transport/interfaces/full_duplex_socket.h create mode 100755 libtransport/src/hicn/transport/interfaces/publication_options.h create mode 100755 libtransport/src/hicn/transport/interfaces/rtc_socket_consumer.cc create mode 100755 libtransport/src/hicn/transport/interfaces/rtc_socket_consumer.h create mode 100755 libtransport/src/hicn/transport/interfaces/rtc_socket_producer.cc create mode 100755 libtransport/src/hicn/transport/interfaces/rtc_socket_producer.h create mode 100755 libtransport/src/hicn/transport/interfaces/socket.h create mode 100755 libtransport/src/hicn/transport/interfaces/socket_consumer.cc create mode 100755 libtransport/src/hicn/transport/interfaces/socket_consumer.h create mode 100755 libtransport/src/hicn/transport/interfaces/socket_options_default_values.h create mode 100755 libtransport/src/hicn/transport/interfaces/socket_options_keys.h create mode 100755 libtransport/src/hicn/transport/interfaces/socket_producer.cc create mode 100755 libtransport/src/hicn/transport/interfaces/socket_producer.h create mode 100755 libtransport/src/hicn/transport/portability/CMakeLists.txt create mode 100755 libtransport/src/hicn/transport/portability/c_portability.h create mode 100755 libtransport/src/hicn/transport/portability/portability.h create mode 100755 libtransport/src/hicn/transport/protocols/CMakeLists.txt create mode 100755 libtransport/src/hicn/transport/protocols/cbr.cc create mode 100755 libtransport/src/hicn/transport/protocols/cbr.h create mode 100755 libtransport/src/hicn/transport/protocols/consumer.conf create mode 100755 libtransport/src/hicn/transport/protocols/download_observer.h create mode 100755 libtransport/src/hicn/transport/protocols/protocol.cc create mode 100755 libtransport/src/hicn/transport/protocols/protocol.h create mode 100755 libtransport/src/hicn/transport/protocols/raaqm.cc create mode 100755 libtransport/src/hicn/transport/protocols/raaqm.h create mode 100755 libtransport/src/hicn/transport/protocols/raaqm_data_path.cc create mode 100755 libtransport/src/hicn/transport/protocols/raaqm_data_path.h create mode 100755 libtransport/src/hicn/transport/protocols/rate_estimation.cc create mode 100755 libtransport/src/hicn/transport/protocols/rate_estimation.h create mode 100755 libtransport/src/hicn/transport/protocols/rtc.cc create mode 100755 libtransport/src/hicn/transport/protocols/rtc.h create mode 100755 libtransport/src/hicn/transport/protocols/rtc_data_path.cc create mode 100755 libtransport/src/hicn/transport/protocols/rtc_data_path.h create mode 100755 libtransport/src/hicn/transport/protocols/test/CMakeLists.txt create mode 100755 libtransport/src/hicn/transport/protocols/test/test_transport_producer.cc create mode 100755 libtransport/src/hicn/transport/protocols/vegas.cc create mode 100755 libtransport/src/hicn/transport/protocols/vegas.h create mode 100755 libtransport/src/hicn/transport/protocols/vegas_rto_estimator.cc create mode 100755 libtransport/src/hicn/transport/protocols/vegas_rto_estimator.h create mode 100755 libtransport/src/hicn/transport/utils/CMakeLists.txt create mode 100755 libtransport/src/hicn/transport/utils/array.h create mode 100755 libtransport/src/hicn/transport/utils/branch_prediction.h create mode 100755 libtransport/src/hicn/transport/utils/content_store.cc create mode 100755 libtransport/src/hicn/transport/utils/content_store.h create mode 100755 libtransport/src/hicn/transport/utils/conversions.h create mode 100755 libtransport/src/hicn/transport/utils/crypto_hash.h create mode 100755 libtransport/src/hicn/transport/utils/crypto_hash_type.h create mode 100755 libtransport/src/hicn/transport/utils/crypto_hasher.h create mode 100755 libtransport/src/hicn/transport/utils/crypto_suite.h create mode 100755 libtransport/src/hicn/transport/utils/daemonizator.cc create mode 100755 libtransport/src/hicn/transport/utils/daemonizator.h create mode 100755 libtransport/src/hicn/transport/utils/deadline_timer.h create mode 100755 libtransport/src/hicn/transport/utils/endianess.h create mode 100755 libtransport/src/hicn/transport/utils/epoll_event_reactor.cc create mode 100755 libtransport/src/hicn/transport/utils/epoll_event_reactor.h create mode 100755 libtransport/src/hicn/transport/utils/event_reactor.h create mode 100755 libtransport/src/hicn/transport/utils/event_thread.h create mode 100755 libtransport/src/hicn/transport/utils/fd_deadline_timer.h create mode 100755 libtransport/src/hicn/transport/utils/hash.h create mode 100755 libtransport/src/hicn/transport/utils/identity.cc create mode 100755 libtransport/src/hicn/transport/utils/identity.h create mode 100755 libtransport/src/hicn/transport/utils/key_id.h create mode 100755 libtransport/src/hicn/transport/utils/linux.h create mode 100755 libtransport/src/hicn/transport/utils/literals.h create mode 100755 libtransport/src/hicn/transport/utils/log.cc create mode 100755 libtransport/src/hicn/transport/utils/log.h create mode 100755 libtransport/src/hicn/transport/utils/membuf.cc create mode 100755 libtransport/src/hicn/transport/utils/membuf.h create mode 100755 libtransport/src/hicn/transport/utils/min_filter.h create mode 100755 libtransport/src/hicn/transport/utils/object_pool.h create mode 100755 libtransport/src/hicn/transport/utils/ring_buffer.h create mode 100755 libtransport/src/hicn/transport/utils/sharable_vector.h create mode 100755 libtransport/src/hicn/transport/utils/signer.cc create mode 100755 libtransport/src/hicn/transport/utils/signer.h create mode 100755 libtransport/src/hicn/transport/utils/socket.h create mode 100755 libtransport/src/hicn/transport/utils/spinlock.h create mode 100755 libtransport/src/hicn/transport/utils/stream_buffer.h create mode 100755 libtransport/src/hicn/transport/utils/string_tokenizer.cc create mode 100755 libtransport/src/hicn/transport/utils/string_tokenizer.h create mode 100755 libtransport/src/hicn/transport/utils/test.h create mode 100755 libtransport/src/hicn/transport/utils/uri.cc create mode 100755 libtransport/src/hicn/transport/utils/uri.h create mode 100755 libtransport/src/hicn/transport/utils/verifier.cc create mode 100755 libtransport/src/hicn/transport/utils/verifier.h create mode 100755 utils/CMakeLists.txt create mode 100755 utils/src/hiperf.cc create mode 100755 utils/src/ping_client.cc create mode 100755 utils/src/ping_server.cc diff --git a/.gitreview b/.gitreview new file mode 100644 index 000000000..2579a4aa2 --- /dev/null +++ b/.gitreview @@ -0,0 +1,4 @@ +[gerrit] +host=gerrit.fd.io +port=29418 +project=hicn diff --git a/AUTHORS b/AUTHORS new file mode 100755 index 000000000..ab60dbdf4 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,6 @@ +Jordan Augé +Alberto Compagno +Giovanni Conte +Luca Muscariello +Michele Papalini +Mauro Sardara diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 000000000..c47f8e640 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,51 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +project(hicn-fdio) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules") + +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +set(HICN_INCLUDE_DIRS ${PROJECT_BINARY_DIR}/lib) + +## Target names +set(LIBHICN hicn) +set(LIBHICN_SHARED hicn.shared) +set(LIBHICN_LIGHT hicn-light) +set(HICN_LIGHT_CONTROL hicnLightControl) +set(HICN_LIGHT_DAEMON hicnLightDaemon) +set(LIBTRANSPORT transport) +set(LIBTRANSPORT_SHARED transport.shared) + +## HEADER FILES +set(LIBHICN_HEADER_FILES) +set(LIBHICN_LIGHT_HEADER_FILES) +set(LIBTRANSPORT_HEADER_FILES) + +set(SUBDIRS lib hicn-light libtransport utils) + +if (BUILD_VPP_PLUGIN AND "${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" ) +list(APPEND SUBDIRS + hicn-plugin +) +list(APPEND HICN_BINARY_API_INCLUDE_DIRS + ${PROJECT_BINARY_DIR}/hicn-plugin + ${PROJECT_BINARY_DIR}/hicn-plugin/vpp_plugins) +endif() + +foreach(dir ${SUBDIRS}) + add_subdirectory(${dir}) +endforeach() \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100755 index 000000000..d64569567 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100755 index 000000000..c43dbb4ee --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +Hybrid Information-Centric Networking (hICN) +======================== + +## Introduction +hicn is an open source implementation of Cisco's hICN. It includes a network +stack, that implements ICN forwarding path in IPv6, and a transport stack +that implements two main transport protocols and a socket API. +The transport protocols provide one reliable transport service implementaton +and a real-time transport service for audio/video media. + +## Directory layout + +| Directory name | Description | +| ---------------------- | ---------------------------------------------- | +| lib | Core support library | +| hicn-plugin | VPP plugin | +| hicn-light | Lightweight packet forwarder | +| libtransport | Support library with transport layer and API | +| utils | Tools for testing | +| apps | Application examples using hicn stack | + + +## Supported platforms + +- Ubuntu 16.04 LTS (x86_64) +- Ubuntu 18.04 LTS (x86_64) +- Debian Stable/Testing +- Red Hat Enterprise Linux 7 +- CentOS 7 +- Android 8 +- iOS 12 +- macOS 10.12 +- Windows 10 diff --git a/apps/README.md b/apps/README.md new file mode 100755 index 000000000..3d763f02b --- /dev/null +++ b/apps/README.md @@ -0,0 +1,2 @@ +Application examples using hicn stack +================== diff --git a/cmake/Modules/BuildMacros.cmake b/cmake/Modules/BuildMacros.cmake new file mode 100755 index 000000000..14a82fab7 --- /dev/null +++ b/cmake/Modules/BuildMacros.cmake @@ -0,0 +1,146 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +############################## +# Utils for building libraries and executables +# + +macro(build_executable exec) + cmake_parse_arguments(ARG + "NO_INSTALL" + "COMPONENT" + "SOURCES;LINK_LIBRARIES;DEPENDS;DEFINITIONS" + ${ARGN} + ) + + add_executable(${exec} ${ARG_SOURCES}) + if(ARG_LINK_LIBRARIES) + target_link_libraries(${exec} ${ARG_LINK_LIBRARIES}) + endif() + + if(ARG_DEPENDS) + add_dependencies(${exec} ${ARG_DEPENDS}) + endif() + + if(ARG_DEFINITIONS) + target_compile_definitions(${exec} PRIVATE ${ARG_DEFINITIONS}) + endif() + + if(NOT ARG_NO_INSTALL) + install(TARGETS ${exec} DESTINATION bin COMPONENT ${ARG_COMPONENT}) + endif() +endmacro() + +macro(build_library lib) + cmake_parse_arguments(ARG + "SHARED;STATIC" + "COMPONENT" + "SOURCES;LINK_LIBRARIES;INSTALL_HEADERS;DEPENDS;INCLUDE_DIRS;DEFINITIONS;INSTALL_ROOT_DIR" + ${ARGN} + ) + + if (ARG_SHARED) + list(APPEND TARGET_LIBS + ${lib}.shared + ) + add_library(${lib}.shared SHARED ${ARG_SOURCES}) + endif() + + if(ARG_STATIC) + list(APPEND TARGET_LIBS + ${lib} + ) + add_library(${lib} STATIC ${ARG_SOURCES}) + endif() + + foreach(library ${TARGET_LIBS}) + target_compile_options(${library} PRIVATE -Wall) + + if(HICN_VERSION) + set_target_properties(${library} + PROPERTIES + SOVERSION ${HICN_VERSION} + ) + endif() + + set_target_properties(${library} + PROPERTIES + OUTPUT_NAME ${lib} + ) + + # library deps + if(ARG_LINK_LIBRARIES) + target_link_libraries(${library} ${ARG_LINK_LIBRARIES}) + endif() + + if(ARG_DEFINITIONS) + target_compile_definitions(${library} PRIVATE ${ARG_DEFINITIONS}) + endif() + + if(ARG_INCLUDE_DIRS) + target_include_directories(${library} BEFORE PUBLIC + ${ARG_INCLUDE_DIRS} + ${PROJECT_BINARY_DIR} + ) + endif() + + # install .so + if(NOT ARG_COMPONENT) + set(ARG_COMPONENT hicn) + endif() + install( + TARGETS ${library} + DESTINATION lib + COMPONENT ${ARG_COMPONENT} + ) + + if(ARG_DEPENDS) + add_dependencies(${library} ${ARG_DEPENDS}) + endif() + endforeach() + + # install headers + if(ARG_INSTALL_HEADERS) + + if (NOT ARG_INSTALL_ROOT_DIR) + set(ARG_INSTALL_ROOT_DIR "hicn") + endif() + + foreach(file ${ARG_INSTALL_HEADERS}) + get_filename_component(_dir ${file} DIRECTORY) + get_filename_component(dir ${_dir} NAME) + if (${dir} STREQUAL src) + set(dir "") + endif() + install( + FILES ${file} + DESTINATION include/${ARG_INSTALL_ROOT_DIR}/${dir} + COMPONENT ${ARG_COMPONENT}-dev + ) + endforeach() + endif() +endmacro() + +add_custom_target(${PROJECT_NAME}_cleanup_profiling_data + "find" "." "-name" "*.gcda" "-delete" + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Cleanup previous profiling data." +) + +macro(AddTest testFile) + add_executable(${ARGV0} ${ARGV0}.cc) + target_link_libraries(${ARGV0} ${TARGET_TRANSPORT_STATIC} ${GTEST_LIBRARIES}) + add_test(${ARGV0} ${ARGV0}) + set_target_properties(${ARGV0} PROPERTIES FOLDER Test) + add_dependencies(${ARGV0} ${PROJECT_NAME}_cleanup_profiling_data) +endmacro(AddTest) \ No newline at end of file diff --git a/cmake/Modules/FindAsio.cmake b/cmake/Modules/FindAsio.cmake new file mode 100755 index 000000000..73888e519 --- /dev/null +++ b/cmake/Modules/FindAsio.cmake @@ -0,0 +1,41 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +######################################## +# +# Find the hcin libraries and includes +# This module sets: +# ASIO_FOUND: True if asio was found +# ASIO_INCLUDE_DIR: The asio include dir +# + +set(ASIO_SEARCH_PATH_LIST + ${ASIO_HOME} + $ENV{ASIO_HOME} + /usr/local + /opt + /usr +) + +find_path(ASIO_INCLUDE_DIR asio.hpp + HINTS ${ASIO_SEARCH_PATH_LIST} + PATH_SUFFIXES include + DOC "Find the asio includes" +) + +set(ASIO_INCLUDE_DIRS ${ASIO_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Asio + REQUIRED_VARS ASIO_INCLUDE_DIRS +) \ No newline at end of file diff --git a/cmake/Modules/FindGFlags.cmake b/cmake/Modules/FindGFlags.cmake new file mode 100755 index 000000000..804bfebdc --- /dev/null +++ b/cmake/Modules/FindGFlags.cmake @@ -0,0 +1,36 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Find libgflags +# +# LIBGFLAGS_INCLUDE_DIR - where to find gflags/gflags.h, etc. +# LIBGFLAGS_LIBRARY - List of libraries when using libgflags. +# LIBGFLAGS_FOUND - True if libgflags found. + + +IF (LIBGFLAGS_INCLUDE_DIR) + # Already in cache, be silent + SET(LIBGFLAGS_FIND_QUIETLY TRUE) +ENDIF () + +FIND_PATH(LIBGFLAGS_INCLUDE_DIR gflags/gflags.h) + +FIND_LIBRARY(LIBGFLAGS_LIBRARY NAMES gflags gflags_static) + +# handle the QUIETLY and REQUIRED arguments and set LIBGFLAGS_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBGFLAGS DEFAULT_MSG LIBGFLAGS_LIBRARY LIBGFLAGS_INCLUDE_DIR) + +MARK_AS_ADVANCED(LIBGFLAGS_LIBRARY LIBGFLAGS_INCLUDE_DIR) \ No newline at end of file diff --git a/cmake/Modules/FindGlog.cmake b/cmake/Modules/FindGlog.cmake new file mode 100755 index 000000000..10023a187 --- /dev/null +++ b/cmake/Modules/FindGlog.cmake @@ -0,0 +1,36 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Find libglog +# +# LIBGLOG_INCLUDE_DIR - where to find glog/logging.h, etc. +# LIBGLOG_LIBRARY - List of libraries when using libglog. +# LIBGLOG_FOUND - True if libglog found. + + +IF (LIBGLOG_INCLUDE_DIR) + # Already in cache, be silent + SET(LIBGLOG_FIND_QUIETLY TRUE) +ENDIF () + +FIND_PATH(LIBGLOG_INCLUDE_DIR glog/logging.h) + +FIND_LIBRARY(LIBGLOG_LIBRARY glog) + +# handle the QUIETLY and REQUIRED arguments and set LIBGLOG_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBGLOG DEFAULT_MSG LIBGLOG_LIBRARY LIBGLOG_INCLUDE_DIR) + +MARK_AS_ADVANCED(LIBGLOG_LIBRARY LIBGLOG_INCLUDE_DIR) \ No newline at end of file diff --git a/cmake/Modules/FindHicnBinaryApi.cmake b/cmake/Modules/FindHicnBinaryApi.cmake new file mode 100755 index 000000000..86a96ea19 --- /dev/null +++ b/cmake/Modules/FindHicnBinaryApi.cmake @@ -0,0 +1,31 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(HICN_BINARY_API_SEARCH_PATH_LIST + ${HICN_BINARY_API_HOME} + $ENV{HICN_BINARY_API_HOME} + /usr/local + /opt + /usr +) + +find_path(HICN_BINARY_API_INCLUDE_DIR vpp_plugins/hicn/hicn_api.h + HINTS ${VPP_SEARCH_PATH_LIST} + PATH_SUFFIXES include + DOC "Find the VPP includes" +) + +set(HICN_BINARY_API_INCLUDE_DIRS ${VPP_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(HicnBinaryApi DEFAULT_MSG VPP_LIBRARIES VPP_INCLUDE_DIRS) \ No newline at end of file diff --git a/cmake/Modules/FindLibEvent.cmake b/cmake/Modules/FindLibEvent.cmake new file mode 100755 index 000000000..5e4113716 --- /dev/null +++ b/cmake/Modules/FindLibEvent.cmake @@ -0,0 +1,55 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +######################################## +# +# Find the LibEvent libraries and includes +# This module sets: +# LIBEVENT_FOUND: True if LibEvent was found +# LIBEVENT_LIBRARY: The LibEvent library +# LIBEVENT_LIBRARIES: The LibEvent library and dependencies +# LIBEVENT_INCLUDE_DIR: The LibEvent include dir +# +# This module will look for the libraries in various locations +# See the LIBEVENT_SEARCH_PATH_LIST for a full list. +# +# The caller can hint at locations using the following variables: +# +# LIBEVENT_HOME (passed as -D to cmake) +# LIBEVENT_HOME (in environment) +# + +set(LIBEVENT_SEARCH_PATH_LIST + ${LIBEVENT_HOME} + $ENV{DEPENDENCIES} + $ENV{LIBEVENT_HOME} + /usr/local + /opt + /usr + ) + +find_path(LIBEVENT_INCLUDE_DIR event2/event.h + HINTS ${LIBEVENT_SEARCH_PATH_LIST} + PATH_SUFFIXES include + DOC "Find the LibEvent includes" ) + +find_library(LIBEVENT_LIBRARY NAMES event + HINTS ${LIBEVENT_SEARCH_PATH_LIST} + PATH_SUFFIXES lib + DOC "Find the LibEvent libraries" ) + +set(LIBEVENT_LIBRARIES ${LIBEVENT_LIBRARY}) +set(LIBEVENT_INCLUDE_DIRS ${LIBEVENT_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LibEvent DEFAULT_MSG LIBEVENT_LIBRARY LIBEVENT_INCLUDE_DIR) diff --git a/cmake/Modules/FindLibhicn.cmake b/cmake/Modules/FindLibhicn.cmake new file mode 100755 index 000000000..7cfaaa5e5 --- /dev/null +++ b/cmake/Modules/FindLibhicn.cmake @@ -0,0 +1,49 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +######################################## +# +# Find the hcin libraries and includes +# This module sets: +# HICN_FOUND: True if hicn was found +# HICN_LIBRARY: The hicn library +# HICN_LIBRARIES: The hicn library and dependencies +# HCIN_INCLUDE_DIR: The hicn include dir +# + +set(HICN_SEARCH_PATH_LIST + ${HICN_HOME} + $ENV{HICN_HOME} + $ENV{FOUNDATION_HOME} + /usr/local + /opt + /usr +) + +find_path(HICN_INCLUDE_DIR hicn/hicn.h + HINTS ${HICN_SEARCH_PATH_LIST} + PATH_SUFFIXES include + DOC "Find the hicn includes" +) + +find_library(HICN_LIBRARY NAMES hicn + HINTS ${HICN_SEARCH_PATH_LIST} + PATH_SUFFIXES lib + DOC "Find the hicn libraries" +) + +set(HICN_LIBRARIES ${HICN_LIBRARY}) +set(HICN_INCLUDE_DIRS ${HICN_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(hicn DEFAULT_MSG HICN_LIBRARY HICN_INCLUDE_DIR) diff --git a/cmake/Modules/FindLibmemif.cmake b/cmake/Modules/FindLibmemif.cmake new file mode 100755 index 000000000..48460eecd --- /dev/null +++ b/cmake/Modules/FindLibmemif.cmake @@ -0,0 +1,47 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +######################################## +# +# Find the hcin libraries and includes +# This module sets: +# LIBMEMIF_FOUND: True if core was found +# LIBMEMIF_LIBRARY: The core library +# LIBMEMIF_INCLUDE_DIR: The core include dir +# + +set(LIBMEMIF_SEARCH_PATH_LIST + ${LIBMEMIF_HOME} + $ENV{LIBMEMIF_HOME} + /usr/local + /opt + /usr +) + +find_path(LIBMEMIF_INCLUDE_DIR memif/libmemif.h + HINTS ${LIBMEMIF_SEARCH_PATH_LIST} + PATH_SUFFIXES include + DOC "Find the libmemif includes" +) + +find_library(LIBMEMIF_LIBRARY NAMES memif + HINTS ${LIBMEMIF_SEARCH_PATH_LIST} + PATH_SUFFIXES lib + DOC "Find the libmemif libraries" +) + +set(LIBMEMIF_LIBRARIES ${LIBMEMIF_LIBRARY}) +set(LIBMEMIF_INCLUDE_DIRS ${LIBMEMIF_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Libmemif DEFAULT_MSG LIBMEMIF_LIBRARY LIBMEMIF_INCLUDE_DIR) diff --git a/cmake/Modules/FindLibparc.cmake b/cmake/Modules/FindLibparc.cmake new file mode 100755 index 000000000..c5c99af15 --- /dev/null +++ b/cmake/Modules/FindLibparc.cmake @@ -0,0 +1,50 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +######################################## +# +# Find the Libparc libraries and includes +# This module sets: +# LIBPARC_FOUND: True if Libparc was found +# LIBPARC_LIBRARY: The Libparc library +# LIBPARC_LIBRARIES: The Libparc library and dependencies +# LIBPARC_INCLUDE_DIR: The Libparc include dir +# + +set(LIBPARC_SEARCH_PATH_LIST + ${LIBPARC_HOME} + $ENV{LIBPARC_HOME} + /usr/local + /opt + /usr +) + +find_path(LIBPARC_INCLUDE_DIR parc/libparc_About.h + HINTS ${LIBPARC_SEARCH_PATH_LIST} + PATH_SUFFIXES include + DOC "Find the Libparc includes" +) + +find_library(LIBPARC_LIBRARY NAMES parc + HINTS ${LIBPARC_SEARCH_PATH_LIST} + PATH_SUFFIXES lib + DOC "Find the Libparc libraries" +) + +set(LIBPARC_LIBRARIES ${LIBPARC_LIBRARY}) +set(LIBPARC_INCLUDE_DIRS ${LIBPARC_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Libparc DEFAULT_MSG LIBPARC_LIBRARY LIBPARC_INCLUDE_DIR) + +mark_as_advanced(LIBPARC_LIBRARY LIBPARC_INCLUDE_DIR) diff --git a/cmake/Modules/FindLibtransport.cmake b/cmake/Modules/FindLibtransport.cmake new file mode 100755 index 000000000..5910a64da --- /dev/null +++ b/cmake/Modules/FindLibtransport.cmake @@ -0,0 +1,48 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +######################################## +# +# Find the LibTRANSPORT libraries and includes +# This module sets: +# LIBTRANSPORT_FOUND: True if Libconsumer-producer was found +# LIBTRANSPORTR_LIBRARY: The Libconsumer-producer library +# LIBTRANSPORT_LIBRARIES: The Libconsumer-producer library and dependencies +# LIBTRANSPORT_INCLUDE_DIR: The Libconsumer-producer include dir +# + +set(LIBTRANSPORT_SEARCH_PATH_LIST + ${LIBTRANSPORT_HOME} + $ENV{LIBTRANSPORTHOME} + /usr/local + /opt + /usr +) + +find_path(LIBTRANSPORT_INCLUDE_DIR hicn/transport/config.h + HINTS ${LIBTRANSPORT_SEARCH_PATH_LIST} + PATH_SUFFIXES include + DOC "Find the libtransport includes" +) + +find_library(LIBTRANSPORT_LIBRARY NAMES transport + HINTS ${LIBTRANSPORT_SEARCH_PATH_LIST} + PATH_SUFFIXES lib + DOC "Find the libtransport libraries" +) + +set(LIBTRANSPORT_LIBRARIES ${LIBTRANSPORT_LIBRARY}) +set(LIBTRANSPORT_INCLUDE_DIRS ${LIBTRANSPORT_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Libtransport DEFAULT_MSG LIBTRANSPORT_LIBRARIES LIBTRANSPORT_INCLUDE_DIRS) \ No newline at end of file diff --git a/cmake/Modules/FindLongBow.cmake b/cmake/Modules/FindLongBow.cmake new file mode 100755 index 000000000..4a05d7fdf --- /dev/null +++ b/cmake/Modules/FindLongBow.cmake @@ -0,0 +1,55 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +######################################## +# +# Find the LongBow libraries and includes +# This module sets: +# LONGBOW_FOUND: True if LongBow was found +# LONGBOW_LIBRARY: The LongBow library +# LONGBOW_LIBRARIES: The LongBow library and dependencies +# LONGBOW_INCLUDE_DIR: The LongBow include dir +# + +set(LONGBOW_SEARCH_PATH_LIST + ${LONGBOW_HOME} + $ENV{LONGBOW_HOME} + $ENV{PARC_HOME} + $ENV{FOUNDATION_HOME} + /usr/local/parc + /usr/local/ccn + /usr/local + /opt + /usr + ) + +find_path(LONGBOW_INCLUDE_DIR LongBow/longBow_About.h + HINTS ${LONGBOW_SEARCH_PATH_LIST} + PATH_SUFFIXES include + DOC "Find the LongBow includes" ) + +find_library(LONGBOW_LIBRARY NAMES longbow + HINTS ${LONGBOW_SEARCH_PATH_LIST} + PATH_SUFFIXES lib + DOC "Find the LongBow libraries" ) + +find_library(LONGBOW_REPORT_LIBRARY NAMES longbow-textplain longbow-ansiterm + HINTS ${LONGBOW_SEARCH_PATH_LIST} + PATH_SUFFIXES lib + DOC "Find the LongBow report libraries" ) + +set(LONGBOW_LIBRARIES ${LONGBOW_LIBRARY} ${LONGBOW_REPORT_LIBRARY}) +set(LONGBOW_INCLUDE_DIRS ${LONGBOW_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LongBow DEFAULT_MSG LONGBOW_LIBRARY LONGBOW_INCLUDE_DIR) diff --git a/cmake/Modules/FindUncrustify.cmake b/cmake/Modules/FindUncrustify.cmake new file mode 100755 index 000000000..f8f6b00b8 --- /dev/null +++ b/cmake/Modules/FindUncrustify.cmake @@ -0,0 +1,21 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Find uncrustify program +# +find_program( UNCRUSTIFY_BIN uncrustify + PATHS + $ENV{UNCRUSTIFY_HOME} + ) + +message( "-- UNCRUSTIFY found in ${UNCRUSTIFY_BIN}" ) diff --git a/cmake/Modules/FindVpp.cmake b/cmake/Modules/FindVpp.cmake new file mode 100755 index 000000000..ae11c8019 --- /dev/null +++ b/cmake/Modules/FindVpp.cmake @@ -0,0 +1,67 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(VPP_SEARCH_PATH_LIST + ${VPP_HOME} + $ENV{VPP_HOME} + /usr/local + /opt + /usr +) + +find_path(VPP_INCLUDE_DIR vnet/vnet.h + HINTS ${VPP_SEARCH_PATH_LIST} + PATH_SUFFIXES include + DOC "Find the VPP includes" +) + +find_library(VPP_LIBRARY_MEMORYCLIENT + NAMES vlibmemoryclient + HINTS ${VPP_SEARCH_PATH_LIST} + PATH_SUFFIXES lib lib64 + DOC "Find the Vpp Memoryclient library" +) + +find_library(VPP_LIBRARY_SVM + NAMES svm + HINTS ${VPP_SEARCH_PATH_LIST} + PATH_SUFFIXES lib lib64 + DOC "Find the Vpp svm library" +) + +find_library(VPP_LIBRARY_INFRA + NAMES vppinfra + HINTS ${VPP_SEARCH_PATH_LIST} + PATH_SUFFIXES lib lib64 + DOC "Find the Vpp infra library" +) + +find_library(VPP_LIBRARY_VATPLUGIN + NAMES vatplugin + HINTS ${VPP_SEARCH_PATH_LIST} + PATH_SUFFIXES lib lib64 + DOC "Find the Vpp vatplugin library" +) + +find_library(VPP_LIBRARY_VLIB + NAMES vlib + HINTS ${VPP_SEARCH_PATH_LIST} + PATH_SUFFIXES lib lib64 + DOC "Find the Vpp vlib library" +) + +set(VPP_LIBRARIES ${VPP_LIBRARY_MEMORYCLIENT} ${VPP_LIBRARY_SVM} ${VPP_LIBRARY_INFRA} ${VPP_LIBRARY_VATPLUGIN} ${VPP_LIBRARY_VLIB}) +set(VPP_INCLUDE_DIRS ${VPP_INCLUDE_DIR} ${VPP_INCLUDE_DIR}/vpp_plugins) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Vpp DEFAULT_MSG VPP_LIBRARIES VPP_INCLUDE_DIRS) \ No newline at end of file diff --git a/cmake/Modules/IosMacros.cmake b/cmake/Modules/IosMacros.cmake new file mode 100755 index 000000000..b1e5cc438 --- /dev/null +++ b/cmake/Modules/IosMacros.cmake @@ -0,0 +1,24 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if(COMPILE_FOR_IOS) + include_directories(iOS) +endif() + +macro(find_package_wrapper) + if(COMPILE_FOR_IOS) + find_host_package(${ARGN}) + else() + find_package(${ARGN}) + endif() +endmacro() \ No newline at end of file diff --git a/cmake/Modules/Packager.cmake b/cmake/Modules/Packager.cmake new file mode 100755 index 000000000..58530fa72 --- /dev/null +++ b/cmake/Modules/Packager.cmake @@ -0,0 +1,105 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +############# +# RPM/DEB/TGZ Packaging utils +# + +set(CONTACT "hicn-dev@lists.fd.io" CACHE STRING "Contact") +set(PACKAGE_MAINTAINER "ICN Team" CACHE STRING "Maintainer") +set(PACKAGE_VENDOR "fd.io" CACHE STRING "Vendor") + +macro(add_package name) + cmake_parse_arguments(ARG + "" + "NAME;DESCRIPION;DEPENDENCIES" + "" + ${ARGN} + ) + + if (0) + # parse /etc/os-release + file(READ "/etc/os-release" os_version) + string(REPLACE "\n" ";" os_version ${os_version}) + foreach(_ver ${os_version}) + string(REPLACE "=" ";" _ver ${_ver}) + list(GET _ver 0 _name) + list(GET _ver 1 _value) + set(OS_${_name} ${_value}) + endforeach() + + # extract version from git + execute_process( + COMMAND git describe --long --match v* + OUTPUT_VARIABLE VER + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + if (NOT VER) + set(VER 1.0) + endif() + + string(REGEX REPLACE "v(.*)-([0-9]+)-(g[0-9a-f]+)" "\\1;\\2;\\3" VER ${VER}) + list(GET VER 0 tag) + string(REPLACE "-" "~" tag ${tag}) + list(GET VER 1 commit_num) + list(GET VER 2 commit_name) + + #define DEB and RPM version numbers + if(${commit_num} EQUAL 0) + set(deb_ver "${tag}") + set(rpm_ver "${tag}") + else() + set(deb_ver "${tag}~${commit_num}~${commit_name}") + set(rpm_ver "${tag}~${commit_num}_${commit_name}") + endif() + + get_cmake_property(components COMPONENTS) + + if(OS_ID_LIKE MATCHES "debian") + set(CPACK_GENERATOR "DEB") + set(type "DEBIAN") + set(CPACK_PACKAGE_VERSION "${deb_ver}") + set(CPACK_DEBIAN_PACKAGE_MAINTAINER "VPP Team") + set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT) + foreach(lc ${components}) + string(TOUPPER ${lc} uc) + set(CPACK_DEBIAN_${uc}_PACKAGE_NAME "${lc}") + endforeach() + elseif(OS_ID_LIKE MATCHES "rhel") + set(CPACK_GENERATOR "RPM") + set(type "RPM") + set(CPACK_PACKAGE_VERSION "${rpm_ver}") + set(CPACK_RPM_FILE_NAME RPM-DEFAULT) + foreach(lc ${components}) + string(TOUPPER ${lc} uc) + if(${lc} MATCHES ".*-dev") + set(CPACK_RPM_${uc}_DEBUGINFO_PACKAGE ON) + set(lc ${lc}el) + endif() + set(CPACK_RPM_${uc}_PACKAGE_NAME "${lc}") + endforeach() + endif() + + if(CPACK_GENERATOR) + set(CPACK_PACKAGE_NAME ${ARG_NAME}) + set(CPACK_STRIP_FILES OFF) + set(CPACK_PACKAGE_VENDOR "${ARG_VENDOR}") + set(CPACK_COMPONENTS_IGNORE_GROUPS 1) + set(CPACK_${CPACK_GENERATOR}_COMPONENT_INSTALL ON) + set(CPACK_${type}_PACKAGE_DESCRIPTION "${ARG_DESCRIPTION}") + set(CPACK_${type}_PACKAGE_RELEASE 1) + include(CPack) + endif() + endif() +endmacro() \ No newline at end of file diff --git a/cmake/Modules/detectCacheSize.cmake b/cmake/Modules/detectCacheSize.cmake new file mode 100755 index 000000000..a8209bb27 --- /dev/null +++ b/cmake/Modules/detectCacheSize.cmake @@ -0,0 +1,21 @@ +# Detect the cache size +# +# XXX: TODO: This is a bug when cross compiling. We are detecting the local +# Cache Line size and not the target cache line size. We should provide some +# way to define this + +set(LEVEL1_DCACHE_LINESIZE 32) + +if( APPLE ) + execute_process(COMMAND sysctl -n hw.cachelinesize + OUTPUT_VARIABLE LEVEL1_DCACHE_LINESIZE + OUTPUT_STRIP_TRAILING_WHITESPACE) +endif( APPLE ) + +if( ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" ) + execute_process(COMMAND getconf LEVEL1_DCACHE_LINESIZE + OUTPUT_VARIABLE LEVEL1_DCACHE_LINESIZE + OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() + +message(STATUS "Cache line size: ${LEVEL1_DCACHE_LINESIZE}") diff --git a/hicn-light/CMakeLists.txt b/hicn-light/CMakeLists.txt new file mode 100755 index 000000000..289e07ecc --- /dev/null +++ b/hicn-light/CMakeLists.txt @@ -0,0 +1,74 @@ +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +project(hicn-light) + +if (NOT CMAKE_BUILD_TYPE) + message(STATUS "No build type selected, default to Release") + set(CMAKE_BUILD_TYPE "Release") +endif() + +set(CMAKE_MODULE_PATH + ${CMAKE_MODULE_PATH} + "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/Modules" +) + +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD_REQUIRED ON) + +include( CTest ) +include( detectCacheSize ) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") +if(ANDROID_API) + message("############ Detected cross compile for $ENV{CMAKE_SYSTEM_NAME}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ANDROID_C_FLAGS}") + endif() + +set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DLIBRTA_DISABLE_VALIDATION -DPARCLibrary_DISABLE_VALIDATION") + +include(IosMacros) + +find_package_wrapper(Libparc REQUIRED) + +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + find_package_wrapper(Libhicn REQUIRED) + set(LIBHICN_LIGHT hicn-light) + set(HICN_LIGHT_CONTROL hicnLightControl) + set(HICN_LIGHT_DAEMON hicnLightDaemon) +else() + set(HICN_LIBRARIES ${LIBHICN_SHARED}) + set(DEPENDENCIES + ${LIBHICN} + ${LIBHICN_SHARED} + ) +endif() + +find_package(Threads REQUIRED) + +set(HICN_LIGHT_LINK_LIBRARIES + hicn-light + ${HICN_LIBRARIES} + ${LIBPARC_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} +) + +# Include dirs -- Order does matter! +list(APPEND HICN_LIGHT_INCLUDE_DIRS + ${HICN_INCLUDE_DIRS} + ${LIBPARC_INCLUDE_DIRS} +) + +if (UNIX) + list(APPEND HICN_LIGHT_LINK_LIBRARIES + m + ) +endif() + +set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") + +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") + set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "${CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS} -undefined dynamic_lookup") + message(STATUS "Set \"-undefined dynamic_lookup\" for shared libraries") +endif() + +add_subdirectory(src) \ No newline at end of file diff --git a/hicn-light/README.md b/hicn-light/README.md new file mode 100755 index 000000000..ba7ed77b3 --- /dev/null +++ b/hicn-light/README.md @@ -0,0 +1,302 @@ +hicn-light +======= + +## Introduction ## + +hicn-light is a socket based forwarder + +## Using hicn-light ## + +### Platforms ### + +hicn-light has been tested in: + +- Ubuntu 16.04 (x86_64) +- Debian Testing +- MacOSX 10.12 + +Other platforms and architectures may work. + +### Dependencies ### + +Build dependencies: + +- c99 ( clang / gcc ) +- CMake 3.4 + +Basic dependencies: + +- OpenSSL +- pthreads +- Libevent +- Libparc + +## hicn-light Executables ## + +hicn-light is a set of binary executables that are used to run a forwarder instance. +The forwarder can be run and configured using the commands + +- `hicnLightDaemon` +- `hicnLightControl` + +Use the `-h` option to display the help messages + +### hicn-light Daemon ### + +The command `hicnLightDaemon` runs the hicn-light forwarder. The forwarder can be executed +with the following options: + +``` +hicnLightDaemon [--port port] [--daemon] [--capacity objectStoreSize] [--log facility=level] + [--log-file filename] [--config file] + +Options: + +--port = tcp port for local in-bound connections +--daemon = start as daemon process +--capacity = maximum number of content objects to cache. To disable the cache + objectStoreSize must be 0. + Default vaule for objectStoreSize is 100000 +--log = sets a facility to a given log level. You can have multiple of these. + facilities: all, config, core, io, message, processor + levels: debug, info, notice, warning, error, critical, alert, off + example: hicnLightDaemon --log io=debug --log core=off +--log-file = file to write log messages to (required in daemon mode) +--config = configuration filename +``` + +The configuration file contains configuration lines as per hicnLightControl (see below for all +the available commands). If logging level or content store capacity is set in the configuration +file, it overrides the command_line. When a configuration file is specified, no default listeners +are setup. Only 'add listener' lines in the configuration file matter. + +If no configuration file is specified, hicnLightDaemon will listen on TCP and UDP ports specified +by the --port flag (or default port). It will listen on both IPv4 and IPv6 if available. The +default port for hicn-light is 9695. Commands are expected on port 2001. + +### hicn-light Control ### + +`hicnLightControl` can be used to send command to the hicn-light forwarder and configure it. +The command can be executed in the following way: + +``` +hicnLightControl [commands] + +Options: + -h = This help screen + commands = configuration line to send to hicn-light (use 'help' for list) +``` + +#### Available Commands in hicn-light Control #### + +This is the full list of available commands in `hicnLightControl`. This commands can be used +from the command line running `hicnLightControl` as explained before, or listing them in a +configuration file. + +Information about the commands are also available in the `hicnLightControl` help message. + +`add listener`: creates a TCP or UDP listener with the specified options on the local forwarder. +For local connections (application to hicn-light) we expect a TCP listener. The default port for +the local listener is 9695. + +``` +add listener + + :User defined name for listener, must start with alpha and bealphanum + :tcp | udp + :IPv4 or IPv6 address + :TCP/UDP port + +``` + +`add listener hicn`: creates a hicn listener with the specified options on the local forwarder. + +``` +add listener hicn + + :User defined name for listener, must start with alpha and be alphanum + :IPv4 or IPv6 address + +``` + +`add connection`: creates a TCP or UDP connection on the local forwarder with the specified options. + +``` +add connection + + : tcp | udp + : symbolic name, e.g. 'conn1' (must be unique, start with alpha) + : the IPv4 or IPv6 of the remote system + : the remote TCP/UDP port + : local IP address to bind to + : local TCP/UDP port + +``` +`add connection hicn`: creates an hicn connection on the local forwarder with the specified options. + +``` +add connection hicn + + : symbolic name, e.g. 'conn1' (must be unique, start with alpha) + : the IPv4 or IPv6 of the remote system + : local IP address to bind to + +``` +`list`: lists the connections, routes or listeners available on the local hicn-light forwarder +``` +list + +``` +`add route`: adds a route to the specified connection + +``` +add route + + :The symbolic name for an exgress (must be unique, start with alpha) + : :The egress connection id (see 'help list connections') + : :ipAddress/netmask + : :positive integer representing cost +``` + +`remove connection`: removes the specified connection. At the moment, this commands is available +only for UDP connections, TCP is ignored. + +``` +remove connection + + : tcp | upd. This is the protocol used to create the connection. + :The symbolic name for an exgress (must be unique, start with alpha) + : :The egress connection id (see 'help list connections') + +``` + +`remove route`: remove the specified prefix for a local connection + +``` +remove route + + : the alphanumeric name of a local connection + : the prefix (ipAddress/netmask) to remove +``` + +`cache serve`: enables/disables replies from local content store (if available) + +``` +cache serve +``` +`cache store`: enables/disables the storage of incoming data packets in the local content store +(if available) + +``` +cache store + +``` +`cache clear`: removes all the cached data form the local content store (if available) + +``` +cache clear + +``` +`set strategy`: sets the forwarding strategy for a give prefix. There are 4 different strategies +implemented in hicn-light: + +- random: each interest is forwarded randomly to one of the available output connections +- random_per_dash_segment: the output connection is selected randomly for each DASH segment. + This can be used only for DASH video streams. +- loadbalancer: each interest is forwarded toward the output connection with the lowest number + of pending interests. The pending interest are the interest sent on a certain connection but + not yet satisfied. More information are available in: + G. Carofiglio, M. Gallo, L. Muscariello, M. Papalini, S. Wang, + "Optimal multipath congestion control and request forwarding in information-centric networks", + ICNP 2013. +- loadbalancer_with_delay: implements the same strategy as loadbalancer but it takes into account + also the propagation delay behind each connections. + +``` +set strategy + + : the prefix to which apply the forwarding strategy + : random | random_per_dash_segment | loadbalancer | loadbalancer_with_delay +``` +`set wldr`: turns on/off WLDR on the specified connection. WLDR (Wireless Loss Detiection and + Recovery) is a protocol that can be used to recover losses generated by unreliable wireless + connections, such as WIFI. More information on WLDR are available in: + G. Carofiglio, L. Muscariello, M. Papalini, N. Rozhnova, X. Zeng, + "Leveraging ICN In-network Control for Loss Detection and Recovery in Wireless Mobile networks", + ICN 2016. Notice that WLDR is currently available only for UDP connections. In order to work + properly, WLDR needs to be activated on both side of the connection. + +``` +set wldr + + :The symbolic name for an exgress (must be unique, start with alpha) + : :The egress connection id (see 'help list connections') + +``` +`add punting`: Add punting rules to the forwarders. + +``` +add punting + + : listener symbolic name +
: prefix to add as a punting rule. (example 1234::0/64) +``` +`mapme enable`: enables/disables mapme + +``` +mapme enable +``` +`mapme discovery`: enables/disables mapme discovery + +``` +mapme discovery +``` + +`mapme timescale`: set the timescale value expressed in millisencods + +``` +mapme timescale +``` +`mapme retx`: set the retrasmission time value expressed in millisecond + +``` +mapme retx +``` +`quit`: Exits the interactive shell + +### hicn-light Configuration File Example ### + +This is an example of a simple configuration file for hicn-light. It can be loaded by running +the command `hicnLightDaemon --config configFile.cfg`, assuming the file name is configFile.cfg + +``` +#create a local listener on port 9199. This will be used by the applications to talk +with the forwarder +add listener udp local0 127.0.0.1 9199 + +#create a connection with a remote node +add connection udp conn0 192.168.0.20 12345 127.0.0.1 9199 + +#add a route toward the remote node +add route conn0 192.168.0.20/24 1 +``` + + +## License ## + +This software is distributed under the following license: + +``` +Copyright (c) 2017-2019 Cisco and/or its affiliates. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` diff --git a/hicn-light/config/hicn-light.service b/hicn-light/config/hicn-light.service new file mode 100755 index 000000000..0f976fc6c --- /dev/null +++ b/hicn-light/config/hicn-light.service @@ -0,0 +1,29 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +[Unit] +Description=hicn-light is the cisco hicn socket based forwarder. +#Documentation=man:hicn-light-forwarder + +[Service] +Environment=PORT=9695 +Environment=LOG_FILE=/tmp/hicn_light.log +Environment=CS_SIZE=1000 +Environment=CONFIG=/etc/hicn/hicn_light.conf +# This will overrride the default environment +EnvironmentFile=-/etc/default/source +ExecStart=/usr/bin/hicnLightDaemon --port ${PORT} --log-file ${LOG_FILE} --capacity ${CS_SIZE} --config ${CONFIG} +Restart=on-failure + +[Install] +WantedBy=multi-user.target diff --git a/hicn-light/src/CMakeLists.txt b/hicn-light/src/CMakeLists.txt new file mode 100755 index 000000000..939f38a34 --- /dev/null +++ b/hicn-light/src/CMakeLists.txt @@ -0,0 +1,45 @@ +# Define a few configuration variables that we want accessible in the software + +include(BuildMacros) +configure_file(config.h.in config.h @ONLY) + +if(NOT ANDROID_API AND NOT COMPILE_FOR_IOS) + add_subdirectory(command_line) +endif () + +add_subdirectory(config) +add_subdirectory(content_store) +add_subdirectory(core) +add_subdirectory(io) +add_subdirectory(messenger) +add_subdirectory(platforms) +add_subdirectory(processor) +add_subdirectory(socket) +add_subdirectory(strategies) +add_subdirectory(utils) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_BINARY_DIR}/config.h +) + +set(COMPILER_DEFINITIONS "-DWITH_MAPME -DWITH_MAPME_FIXES") + +list(APPEND HICN_LIGHT_INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. +) + +if (INSTALL_HEADER) + set(TO_INSTALL_HEADERS ${HEADER_FILES}) +endif() + +build_library(${LIBHICN_LIGHT} + STATIC + SOURCES ${SOURCE_FILES} + INSTALL_HEADERS ${TO_INSTALL_HEADERS} + LINK_LIBRARIES ${LIBRARIES} + DEPENDS ${DEPENDENCIES} + COMPONENT hicn-light + INCLUDE_DIRS ${HICN_LIGHT_INCLUDE_DIRS} + INSTALL_ROOT_DIR hicn/hicn-light + DEFINITIONS ${COMPILER_DEFINITIONS} +) diff --git a/hicn-light/src/command_line/CMakeLists.txt b/hicn-light/src/command_line/CMakeLists.txt new file mode 100755 index 000000000..16c23dc5c --- /dev/null +++ b/hicn-light/src/command_line/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(controller) +add_subdirectory(daemon) diff --git a/hicn-light/src/command_line/controller/CMakeLists.txt b/hicn-light/src/command_line/controller/CMakeLists.txt new file mode 100755 index 000000000..b53e610a1 --- /dev/null +++ b/hicn-light/src/command_line/controller/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +list(APPEND CONTROLLER_SRC + hicnLightControl_main.c +) + +build_executable(${HICN_LIGHT_CONTROL} + SOURCES ${CONTROLLER_SRC} + LINK_LIBRARIES ${HICN_LIGHT_LINK_LIBRARIES} + DEPENDS hicn-light + COMPONENT hicn-light +) diff --git a/hicn-light/src/command_line/controller/hicnLightControl_main.c b/hicn-light/src/command_line/controller/hicnLightControl_main.c new file mode 100755 index 000000000..4641bddf5 --- /dev/null +++ b/hicn-light/src/command_line/controller/hicnLightControl_main.c @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +size_t commandOutputLen = 0; // preserve the number of structs composing + // payload in case on not interactive call. + +// REMINDER: when a new_command is added, the following array has to be updated +// with the sizeof(new_command). It allows to allocate the buffer for receiving +// the payload of the DAEMON RESPONSE after the header has beed read. Each +// command identifier (typedef enum command_id) corresponds to a position in the +// following array. +static int payloadLengthController[LAST_COMMAND_VALUE] = { + sizeof(add_listener_command), + sizeof(add_connection_command), + sizeof(list_connections_command), // needed when get response from FWD + sizeof(add_route_command), + sizeof(list_routes_command), // needed when get response from FWD + sizeof(remove_connection_command), + sizeof(remove_route_command), + sizeof(cache_store_command), + sizeof(cache_serve_command), + 0, // cache clear + sizeof(set_strategy_command), + sizeof(set_wldr_command), + sizeof(add_punting_command), + sizeof(list_listeners_command), // needed when get response from FWD + sizeof(mapme_activator_command), + sizeof(mapme_activator_command), + sizeof(mapme_timing_command), + sizeof(mapme_timing_command)}; + +#include +#include +#include +#include + +typedef struct controller_main_state { + ControlState *controlState; +} ControlMainState; + +static void _displayForwarderLogo(void){ + const char cli_banner [] = + "\033[0;31m ____ ___ _ \033[0m __ _ __ _ __ __\n" + "\033[0;31m / __// _ \\ (_)___ \033[0m / / (_)____ ___ ____/ /(_)___ _ / / / /_\n" + "\033[0;31m / _/ / // /_ / // _ \\ \033[0m / _ \\ / // __// _ \\___/ // // _ `// _ \\/ __/\n" + "\033[0;31m/_/ /____/(_)/_/ \\___/ \033[0m/_//_//_/ \\__//_//_/ /_//_/ \\_, //_//_/\\__/\n" + " /___/ \n"; + printf("%s", cli_banner); + printf("\n"); +} + +static void _displayUsage(char *programName) { + printf("Usage: %s -h\n", programName); + printf( + "hicn-light is the 1.0 source, which runs on each end system and as a " + "software source\n"); + printf( + "on intermediate systems. controller is the program to configure the " + "source, daemon.\n"); + printf("\n"); + printf("Options:\n"); + printf("-h = This help screen\n"); + printf( + "commands = configuration line to send to hicn-light (use 'help' " + "for list)\n"); + printf("\n"); +} + +static int _parseArgs(int argc, char *argv[], char **keystorePath, + char **keystorePassword, PARCList *commandList) { + static struct option longFormOptions[] = { + {"help", no_argument, 0, 'h'}, + {"keystore", required_argument, 0, 'k'}, + {"password", required_argument, 0, 'p'}, + {0, 0, 0, 0}}; + + int c; + + while (1) { + // getopt_long stores the option index here. + int optionIndex = 0; + + c = getopt_long(argc, argv, "hk:p:", longFormOptions, &optionIndex); + + // Detect the end of the options. + if (c == -1) { + break; + } + + switch (c) { + case 'k': + *keystorePath = optarg; + break; + + case 'p': + *keystorePassword = optarg; + break; + + case 'h': + default: + _displayUsage(argv[0]); + return 0; + } + } + + // Any remaining parameters get put in the command list. + if (optind < argc) { + while (optind < argc) { + parcList_Add(commandList, argv[optind]); + optind++; + } + } + + return 1; +} + +struct iovec *_writeAndReadMessage(ControlState *state, struct iovec *msg) { + parcAssertNotNull(msg, "Parameter msg must be non-null"); + int sockfd = controlState_GetSockfd(state); + + // check if request has a payload + if (((header_control_message *)msg[0].iov_base)->length > + 0) { // command with payload + // write header + payload (compatibility issue: two write needed instead of + // the writev) + if (write(sockfd, msg[0].iov_base, msg[0].iov_len) < 0 || + write(sockfd, msg[1].iov_base, msg[1].iov_len) < 0) { + printf("\nError while sending the Message: cannot write on socket \n"); + exit(EXIT_FAILURE); + } + parcMemory_Deallocate(&msg[1].iov_base); + } else { // command without payload, e.g. 'list' + // write header only + if (write(sockfd, msg[0].iov_base, msg[0].iov_len) < 0) { + printf("\nError while sending the Message: cannot write on socket \n"); + exit(EXIT_FAILURE); + } + } + parcMemory_Deallocate(&msg[0].iov_base); + + // ======= RECEIVE ======= + + header_control_message *headerResponse = + (header_control_message *)parcMemory_AllocateAndClear( + sizeof(header_control_message)); + if (recv(sockfd, headerResponse, sizeof(header_control_message), 0) < 0) { + printf("\nError in Receiving the Message \n"); + exit(EXIT_FAILURE); + } + + if (headerResponse->messageType < RESPONSE_LIGHT || + headerResponse->messageType >= LAST_MSG_TYPE_VALUE) { + char *checkFinMsg = parcMemory_Reallocate(headerResponse, 32); + if (recv(sockfd, checkFinMsg, sizeof(checkFinMsg), + MSG_PEEK | MSG_DONTWAIT) == 0) { + // if recv returns zero, that means the connection has been closed: + close(sockfd); + printf("\nConnection terminated by the Daemon. Exiting... \n"); + exit(EXIT_SUCCESS); + } else { + printf("\nError: Unrecognized message type received \n"); + exit(EXIT_FAILURE); + } + } + + void *payloadResponse = NULL; + + if ((commandOutputLen = headerResponse->length) > 0) { + payloadResponse = parcMemory_AllocateAndClear( + payloadLengthController[headerResponse->commandID] * + headerResponse->length); + + if (recv(sockfd, payloadResponse, + payloadLengthController[headerResponse->commandID] * + headerResponse->length, + 0) < 0) { + printf("\nError in Receiving the Message \n"); + exit(EXIT_FAILURE); + } + } + + struct iovec *response = + parcMemory_AllocateAndClear(sizeof(struct iovec) * 2); + + response[0].iov_base = headerResponse; + response[0].iov_len = sizeof(header_control_message); + response[1].iov_base = payloadResponse; + response[1].iov_len = payloadLengthController[headerResponse->commandID] * + headerResponse->length; + + return response; +} + +int main(int argc, char *argv[]) { + _displayForwarderLogo(); + + if (argc == 2 && strcmp("-h", argv[1]) == 0) { + _displayUsage(argv[0]); + exit(EXIT_SUCCESS); + } + + PARCList *commands = + parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList); + + if (!_parseArgs(argc, argv, NULL, NULL, commands)) { + parcList_Release(&commands); + exit(EXIT_FAILURE); + } + + ControlMainState mainState; + mainState.controlState = + controlState_Create(&mainState, _writeAndReadMessage, true); + + controlState_RegisterCommand(mainState.controlState, + controlRoot_HelpCreate(mainState.controlState)); + controlState_RegisterCommand(mainState.controlState, + controlRoot_Create(mainState.controlState)); + + if (parcList_Size(commands) > 0) { + controlState_SetInteractiveFlag(mainState.controlState, false); + controlState_DispatchCommand(mainState.controlState, commands); + char **commandOutputMain = + controlState_GetCommandOutput(mainState.controlState); + if (commandOutputMain != NULL && commandOutputLen > 0) { + for (size_t j = 0; j < commandOutputLen; j++) { + printf("Output %zu: %s \n", j, commandOutputMain[j]); + } + controlState_ReleaseCommandOutput(mainState.controlState, + commandOutputMain, commandOutputLen); + } + // release + + } else { + controlState_Interactive(mainState.controlState); + } + + parcList_Release(&commands); + + controlState_Destroy(&mainState.controlState); + + return EXIT_SUCCESS; +} diff --git a/hicn-light/src/command_line/daemon/CMakeLists.txt b/hicn-light/src/command_line/daemon/CMakeLists.txt new file mode 100755 index 000000000..fd6cc9310 --- /dev/null +++ b/hicn-light/src/command_line/daemon/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +list(APPEND DAEMON_SRC + hicnLightDaemon_main.c +) + +build_executable(${HICN_LIGHT_DAEMON} + SOURCES ${DAEMON_SRC} + LINK_LIBRARIES ${HICN_LIGHT_LINK_LIBRARIES} + DEPENDS hicn-light + COMPONENT hicn-light +) \ No newline at end of file diff --git a/hicn-light/src/command_line/daemon/hicnLightDaemon_main.c b/hicn-light/src/command_line/daemon/hicnLightDaemon_main.c new file mode 100755 index 000000000..f6d521711 --- /dev/null +++ b/hicn-light/src/command_line/daemon/hicnLightDaemon_main.c @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +static void _displayForwarderLogo(void){ + const char cli_banner [] = + "\033[0;31m ____ ___ _ \033[0m __ _ __ _ __ __\n" + "\033[0;31m / __// _ \\ (_)___ \033[0m / / (_)____ ___ ____/ /(_)___ _ / / / /_\n" + "\033[0;31m / _/ / // /_ / // _ \\ \033[0m / _ \\ / // __// _ \\___/ // // _ `// _ \\/ __/\n" + "\033[0;31m/_/ /____/(_)/_/ \\___/ \033[0m/_//_//_/ \\__//_//_/ /_//_/ \\_, //_//_/\\__/\n" + " /___/ \n"; + printf("%s", cli_banner); + printf("\n"); +} + +static void _usage(int exitCode) { + printf( + "Usage: daemon [--port port] [--daemon] [--capacity objectStoreSize] " + "[--log facility=level] [--log-file filename] [--config file]\n"); + printf("\n"); + printf( + "hicn-light run as a daemon is the program to launch the forwarder, " + "either as a console program\n"); + printf( + "or a background daemon (detatched from console). Once running, use the " + "program controller to\n"); + printf("configure hicn-light.\n"); + printf("\n"); + printf( + "The configuration file contains configuration lines as per " + "controller\n"); + printf( + "If logging level or content store capacity is set in the configuraiton " + "file, it overrides the command_line\n"); + printf( + "When a configuration file is specified, no default listeners on 'port' " + "are setup. Only 'add listener' lines\n"); + printf("in the configuration file matter.\n"); + printf("\n"); + printf( + "If no configuration file is specified, daemon will listen on TCP and " + "UDP ports specified by\n"); + printf( + "the --port flag (or default port). It will listen on both IPv4 and " + "IPv6 if available.\n"); + printf("\n"); + printf("Options:\n"); + printf("--port = tcp port for in-bound connections\n"); + printf("--daemon = start as daemon process\n"); + printf("--objectStoreSize = maximum number of content objects to cache\n"); + printf( + "--log = sets a facility to a given log level. You can have " + "multiple of these.\n"); + printf( + " facilities: all, config, core, io, message, " + "processor\n"); + printf( + " levels: debug, info, notice, warning, error, " + "critical, alert, off\n"); + printf(" example: daemon --log io=debug --log core=off\n"); + printf( + "--log-file = file to write log messages to (required in daemon " + "mode)\n"); + printf("--config = configuration filename\n"); + printf("\n"); + exit(exitCode); +} + +static void _setLogLevelToLevel(int logLevelArray[LoggerFacility_END], + LoggerFacility facility, + const char *levelString) { + PARCLogLevel level = parcLogLevel_FromString(levelString); + + if (level < PARCLogLevel_All) { + // we have a good facility and level + logLevelArray[facility] = level; + } else { + printf("Invalid log level string %s\n", levelString); + _usage(EXIT_FAILURE); + } +} + +/** + * string: "facility=level" + * Set the right thing in the logger + */ +static void _setLogLevel(int logLevelArray[LoggerFacility_END], + const char *string) { + char *tofree = parcMemory_StringDuplicate(string, strlen(string)); + char *p = tofree; + + char *facilityString = strsep(&p, "="); + if (facilityString) { + char *levelString = p; + + if (strcasecmp(facilityString, "all") == 0) { + for (LoggerFacility facility = 0; facility < LoggerFacility_END; + facility++) { + _setLogLevelToLevel(logLevelArray, facility, levelString); + } + } else { + LoggerFacility facility; + for (facility = 0; facility < LoggerFacility_END; facility++) { + if (strcasecmp(facilityString, logger_FacilityString(facility)) == 0) { + break; + } + } + + if (facility < LoggerFacility_END) { + _setLogLevelToLevel(logLevelArray, facility, levelString); + } else { + printf("Invalid facility string %s\n", facilityString); + _usage(EXIT_FAILURE); + } + } + } + + parcMemory_Deallocate((void **)&tofree); +} + +static void _daemonize(void) { + if (getppid() == 1) { + // already a daemon + return; + } + + int forkReturn = fork(); + parcTrapUnexpectedStateIf(forkReturn < 0, "Fork error"); + + if (forkReturn > 0) { + // parent exits + exit(EXIT_SUCCESS); + } + + // Child daemon detaches + printf("child continuing, pid = %u\n", getpid()); + + // get a new process group independent from old parent + setsid(); + + /* close all descriptors */ + for (int i = getdtablesize(); i >= 0; --i) { + close(i); + } + + // reset errno because it might be seg to EBADF from the close calls above + errno = 0; + + // Redirect stdin and stdout and stderr to /dev/null + const char *devnull = "/dev/null"; + int nullfile = open(devnull, O_RDWR); + parcAssertTrue(nullfile >= 0, "Error opening file '%s': (%d) %s", devnull, + errno, strerror(errno)); + + int ret; + ret = dup(nullfile); + parcAssertTrue(ret == 1, "Error duping fd 1 got %d file: (%d) %s", ret, errno, + strerror(errno)); + ret = dup(nullfile); + parcAssertTrue(ret == 2, "Error duping fd 2, got %d file: (%d) %s", ret, + errno, strerror(errno)); + + // forwarder will capture signals +} + +static Logger *_createLogfile(const char *logfile) { + int logfd = open(logfile, O_WRONLY | O_APPEND | O_CREAT, S_IWUSR | S_IRUSR); + if (logfd < 0) { + fprintf(stderr, "Error opening %s for writing: (%d) %s\n", logfile, errno, + strerror(errno)); + exit(EXIT_FAILURE); + } + + chmod(logfile, S_IRWXU); + + PARCFileOutputStream *fos = parcFileOutputStream_Create(logfd); + PARCOutputStream *pos = parcFileOutputStream_AsOutputStream(fos); + PARCLogReporter *reporter = parcLogReporterFile_Create(pos); + + Logger *logger = logger_Create(reporter, parcClock_Wallclock()); + + parcOutputStream_Release(&pos); + parcLogReporter_Release(&reporter); + + return logger; +} + +int main(int argc, const char *argv[]) { + _displayForwarderLogo(); + + uint16_t port = PORT_NUMBER; + uint16_t configurationPort = 2001; + bool daemon = false; + int capacity = -1; + const char *configFileName = NULL; + + char *logfile = NULL; + + if (argc == 2 && strcasecmp(argv[1], "-h") == 0) { + _usage(EXIT_SUCCESS); + } + + int logLevelArray[LoggerFacility_END]; + for (int i = 0; i < LoggerFacility_END; i++) { + logLevelArray[i] = -1; + } + + for (int i = 0; i < argc; i++) { + if (argv[i][0] == '-') { + if (strcmp(argv[i], "--config") == 0) { + configFileName = argv[i + 1]; + i++; + } else if (strcmp(argv[i], "--port") == 0) { + port = atoi(argv[i + 1]); + i++; + } else if (strcmp(argv[i], "--daemon") == 0) { + daemon = true; + } else if (strcmp(argv[i], "--capacity") == 0 || + strcmp(argv[i], "-c") == 0) { + capacity = atoi(argv[i + 1]); + i++; + } else if (strcmp(argv[i], "--log") == 0) { + _setLogLevel(logLevelArray, argv[i + 1]); + i++; + } else if (strcmp(argv[i], "--log-file") == 0) { + if (logfile) { + // error cannot repeat + fprintf(stderr, "Cannot specify --log-file more than once\n"); + _usage(EXIT_FAILURE); + } + + logfile = parcMemory_StringDuplicate(argv[i + 1], strlen(argv[i + 1])); + i++; + } else { + _usage(EXIT_FAILURE); + } + } + } + + // set restrictive umask, in case we create any files + umask(027); + + if (daemon && (logfile == NULL)) { + fprintf(stderr, "Must specify a logfile when running in daemon mode\n"); + _usage(EXIT_FAILURE); + } + + if (daemon) { + // inside this call, parent will EXIT_SUCCESS and child will continue + _daemonize(); + } + + Logger *logger = NULL; + if (logfile) { + logger = _createLogfile(logfile); + parcMemory_Deallocate((void **)&logfile); + } else { + PARCLogReporter *stdoutReporter = parcLogReporterTextStdout_Create(); + logger = logger_Create(stdoutReporter, parcClock_Wallclock()); + parcLogReporter_Release(&stdoutReporter); + } + + for (int i = 0; i < LoggerFacility_END; i++) { + if (logLevelArray[i] > -1) { + logger_SetLogLevel(logger, i, logLevelArray[i]); + } + } + + // this will update the clock to the tick clock + Forwarder *forwarder = forwarder_Create(logger); + + Configuration *configuration = forwarder_GetConfiguration(forwarder); + + if (capacity > -1) { + configuration_SetObjectStoreSize(configuration, capacity); + } + + if (configFileName) { + forwarder_SetupAllListeners(forwarder, port, NULL); + forwarder_SetupFromConfigFile(forwarder, configFileName); + } else { + // NULL to not setup AF_UNIX + forwarder_SetupAllListeners(forwarder, port, NULL); + } + + Dispatcher *dispatcher = forwarder_GetDispatcher(forwarder); + + logger_Log(logger, LoggerFacility_Core, PARCLogLevel_Alert, "daemon", + "hicn-light running port %d configuration-port %d", port, + configurationPort); + + dispatcher_Run(dispatcher); + + logger_Log(logger, LoggerFacility_Core, PARCLogLevel_Alert, "daemon", + "hicn-light exiting port %d", port); + + forwarder_Destroy(&forwarder); + + sleep(2); + + logger_Release(&logger); + return 0; +} diff --git a/hicn-light/src/config.h.in b/hicn-light/src/config.h.in new file mode 100755 index 000000000..16ec1ab3a --- /dev/null +++ b/hicn-light/src/config.h.in @@ -0,0 +1,5 @@ +/* CPU Cache line size */ +#define LEVEL1_DCACHE_LINESIZE @LEVEL1_DCACHE_LINESIZE@ + +#define _GNU_SOURCE + diff --git a/hicn-light/src/config/CMakeLists.txt b/hicn-light/src/config/CMakeLists.txt new file mode 100755 index 000000000..5ce680bfc --- /dev/null +++ b/hicn-light/src/config/CMakeLists.txt @@ -0,0 +1,97 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/commandOps.h + ${CMAKE_CURRENT_SOURCE_DIR}/commandParser.h + ${CMAKE_CURRENT_SOURCE_DIR}/configuration.h + ${CMAKE_CURRENT_SOURCE_DIR}/commandReturn.h + ${CMAKE_CURRENT_SOURCE_DIR}/symbolicNameTable.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlState.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlRoot.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlAddConnection.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlAdd.h + ${CMAKE_CURRENT_SOURCE_DIR}/configurationFile.h + ${CMAKE_CURRENT_SOURCE_DIR}/configurationListeners.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlAddRoute.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlAddListener.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlListConnections.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlList.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlListListeners.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlListRoutes.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlQuit.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlRemove.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlRemoveConnection.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlRemoveRoute.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlSet.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlUnset.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlSetDebug.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlUnsetDebug.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlMapMe.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlMapMeEnable.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlMapMeDiscovery.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlMapMeTimescale.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlCacheServe.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlCacheStore.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlCacheClear.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlCache.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlSetStrategy.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlSetWldr.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlAddPunting.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlRemovePunting.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/commandOps.c + ${CMAKE_CURRENT_SOURCE_DIR}/commandParser.c + ${CMAKE_CURRENT_SOURCE_DIR}/configuration.c + ${CMAKE_CURRENT_SOURCE_DIR}/configurationFile.c + ${CMAKE_CURRENT_SOURCE_DIR}/configurationListeners.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlState.c + ${CMAKE_CURRENT_SOURCE_DIR}/symbolicNameTable.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlAdd.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlAddConnection.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlAddRoute.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlAddListener.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlList.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlListConnections.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlListListeners.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlListRoutes.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlQuit.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlRemove.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlRemoveConnection.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlRemoveRoute.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlRoot.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlSet.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlSetDebug.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlUnset.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlUnsetDebug.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlMapMe.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlMapMeEnable.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlMapMeDiscovery.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlMapMeTimescale.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlMapMeRetx.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlCacheServe.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlCacheStore.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlCacheClear.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlCache.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlSetStrategy.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlSetWldr.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlAddPunting.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlRemovePunting.c +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/hicn-light/src/config/commandOps.c b/hicn-light/src/config/commandOps.c new file mode 100755 index 000000000..027c86e0a --- /dev/null +++ b/hicn-light/src/config/commandOps.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#ifndef __ANDROID__ +#ifdef HAVE_ERRNO_H +#include +#else +extern int errno; +#endif +#endif + +#include + +#include +#include + +CommandOps *commandOps_Create(void *closure, const char *command, + void (*init)(CommandParser *parser, + CommandOps *ops), + CommandReturn (*execute)(CommandParser *parser, + CommandOps *ops, + PARCList *args), + void (*destroyer)(CommandOps **opsPtr)) { + parcAssertNotNull(command, "Parameter command must be non-null"); + parcAssertNotNull(execute, "Parameter execute must be non-null"); + CommandOps *ops = parcMemory_AllocateAndClear(sizeof(CommandOps)); + parcAssertNotNull(ops, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(CommandOps)); + + ops->closure = closure; + ops->command = parcMemory_StringDuplicate(command, strlen(command) + 1); + ops->init = init; + ops->execute = execute; + ops->destroyer = destroyer; + return ops; +} + +void commandOps_Destroy(CommandOps **opsPtr) { + parcAssertNotNull(opsPtr, "Parameter opsPtr must be non-null"); + parcAssertNotNull(*opsPtr, + "Parameter opsPtr must dereference to non-null pointer"); + + CommandOps *ops = *opsPtr; + parcMemory_Deallocate((void **)&(ops->command)); + // DO NOT call ops->destroyer, we are one! + parcMemory_Deallocate((void **)&ops); + + *opsPtr = NULL; +} diff --git a/hicn-light/src/config/commandOps.h b/hicn-light/src/config/commandOps.h new file mode 100755 index 000000000..6428a3ebf --- /dev/null +++ b/hicn-light/src/config/commandOps.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file command_Ops.h + * @brief The function structure defining a CLI command + * + * The function structure that defines a CLI command. Each command will return + * one of these which defines how to run the command. + * + */ + +#ifndef command_Ops_h +#define command_Ops_h + +#include + +#include + +// forward reference +struct command_parser; + +struct command_ops; +typedef struct command_ops CommandOps; + +/** + * @typedef CommandOps + * @abstract Each command implements a CommandOps + * @constant closure is a user-specified pointer for any state the user needs + * @constant command The text string of the command, must be the spelled out + * string, e.g. "help list routes" + * @constant init A function to call to initialize the command at program + * startup + * @constant execute A function to call to execute the command + * @constant destroyer A function to call to release the command + * @discussion + * Typically, the root of the thee has an Init function that then initilizes + * the rest of the tree. For example: + * + * @code + * const CommandOps control_Root = { + * .closure = NULL, + * .command = "", // empty string for root + * .init = control_Root_Init, + * .execute = control_Root_Execute + * .destroyer = NULL + * }; + * @endcode + * + * The control_Root_Init function will then begin adding the subtree under root. + * For example: + * + * @code + * const CommandOps control_Add = { + * .closure = NULL, + * .command = "add", + * .init = control_Add_Init, + * .execute = control_Add_Execute, + * .destroyer = NULL + * }; + * + * static void + * control_Root_Init(ControlState *state, CommandOps *ops) + * { + * controlState_RegisterCommand(state, &control_Add); + * } + * @endcode + */ +struct command_ops { + void *closure; + char *command; + void (*init)(struct command_parser *parser, CommandOps *ops); + CommandReturn (*execute)(struct command_parser *parser, CommandOps *ops, + PARCList *args); + void (*destroyer)(CommandOps **opsPtr); +}; + +/** + * A helper function to create the pubically defined CommandOps. + * + * Retruns allocated memory of the command + * + * @param [in] command The string is copied + * + * @retval <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +CommandOps *commandOps_Create( + void *closure, const char *command, + void (*init)(struct command_parser *parser, CommandOps *ops), + CommandReturn (*execute)(struct command_parser *parser, CommandOps *ops, + PARCList *args), + void (*destroyer)(CommandOps **opsPtr)); + +/** + * De-allocates the memory of the CommandOps and the copied command string + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @retval <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void commandOps_Destroy(CommandOps **opsPtr); +#endif // command_Ops_h diff --git a/hicn-light/src/config/commandParser.c b/hicn-light/src/config/commandParser.c new file mode 100755 index 000000000..84d273c9d --- /dev/null +++ b/hicn-light/src/config/commandParser.c @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#ifndef __ANDROID__ +#ifdef HAVE_ERRNO_H +#include +#else +extern int errno; +#endif +#endif + +struct command_parser { + // key = command, value = CommandOps + PARCTreeRedBlack *commandTree; + bool debugFlag; +}; + +static int _stringCompare(const void *key1, const void *key2) { + return strcasecmp((const char *)key1, (const char *)key2); +} + +CommandParser *commandParser_Create(void) { + CommandParser *state = parcMemory_AllocateAndClear(sizeof(CommandParser)); + parcAssertNotNull(state, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(CommandParser)); + + state->commandTree = parcTreeRedBlack_Create(_stringCompare, // key compare + NULL, // key free + NULL, // key copy + NULL, // value equals + NULL, // value free + NULL // value copy + ); + state->debugFlag = false; + return state; +} + +void commandParser_Destroy(CommandParser **parserPtr) { + CommandParser *parser = *parserPtr; + + // destroy every element if it has a destroyer + PARCArrayList *values = parcTreeRedBlack_Values(parser->commandTree); + if (values) { + for (int i = 0; i < parcArrayList_Size(values); i++) { + CommandOps *ops = parcArrayList_Get(values, i); + parcTreeRedBlack_Remove(parser->commandTree, ops->command); + if (ops->destroyer) { + ops->destroyer(&ops); + } + } + parcArrayList_Destroy(&values); + } + + parcTreeRedBlack_Destroy(&parser->commandTree); + + parcMemory_Deallocate((void **)&parser); + *parserPtr = NULL; +} + +void commandParser_SetDebug(CommandParser *state, bool debugFlag) { + state->debugFlag = debugFlag; +} + +bool commandParser_GetDebug(CommandParser *state) { return state->debugFlag; } + +void commandParser_RegisterCommand(CommandParser *state, CommandOps *ops) { + parcAssertNotNull(state, "Parameter state must be non-null"); + parcAssertNotNull(ops, "Parameter ops must be non-null"); + parcAssertNotNull(ops->command, "Operation command string must be non-null"); + + void *exists = parcTreeRedBlack_Get(state->commandTree, ops->command); + parcAssertNull(exists, "Command '%s' already exists in the tree %p\n", + ops->command, (void *)exists); + + parcTreeRedBlack_Insert(state->commandTree, (void *)ops->command, + (void *)ops); + + // if the command being registered asked for an init function to be called, + // call it + if (ops->init != NULL) { + ops->init(state, ops); + } +} + +static PARCList *parseStringIntoTokens(const char *originalString) { + PARCList *list = + parcList(parcArrayList_Create(parcArrayList_StdlibFreeFunction), + PARCArrayListAsPARCList); + + char *token; + + char *tofree = + parcMemory_StringDuplicate(originalString, strlen(originalString) + 1); + char *string = tofree; + + while ((token = strsep(&string, " \t\n")) != NULL) { + if (strlen(token) > 0) { + parcList_Add(list, strdup(token)); + } + } + + parcMemory_Deallocate((void **)&tofree); + + return list; +} + +/** + * Matches the user arguments to available commands, returning the command or + * NULL if not found + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +static CommandOps *commandParser_MatchCommand(CommandParser *state, + PARCList *args) { + // Find the longest matching prefix command. + // Pretty wildly inefficient + + size_t longest_token_count = 0; + char *longest_command = NULL; + + PARCArrayList *commands = parcTreeRedBlack_Keys(state->commandTree); + for (int i = 0; i < parcArrayList_Size(commands); i++) { + char *command = parcArrayList_Get(commands, i); + PARCList *command_tokens = parseStringIntoTokens(command); + + // is it a prefix match? + if (parcList_Size(args) >= parcList_Size(command_tokens)) { + bool possible_match = true; + for (int i = 0; i < parcList_Size(command_tokens) && possible_match; + i++) { + const char *a = parcList_GetAtIndex(command_tokens, i); + const char *b = parcList_GetAtIndex(args, i); + if (strncasecmp(a, b, strlen(a) + 1) != 0) { + possible_match = false; + } + } + + if (possible_match && + parcList_Size(command_tokens) > longest_token_count) { + longest_token_count = parcList_Size(command_tokens); + longest_command = command; + } + } + + parcList_Release(&command_tokens); + } + + parcArrayList_Destroy(&commands); + + if (longest_token_count == 0) { + return NULL; + } else { + CommandOps *ops = parcTreeRedBlack_Get(state->commandTree, longest_command); + parcAssertNotNull(ops, "Got null operations for command '%s'\n", + longest_command); + return ops; + } +} + +CommandReturn commandParser_DispatchCommand(CommandParser *state, + PARCList *args) { + CommandOps *ops = commandParser_MatchCommand(state, args); + + if (ops == NULL) { + printf("Command not found.\n"); + return CommandReturn_Failure; + } else { + return ops->execute(state, ops, args); + } +} + +bool commandParser_ContainsCommand(CommandParser *parser, const char *command) { + CommandOps *ops = parcTreeRedBlack_Get(parser->commandTree, command); + return (ops != NULL); +} diff --git a/hicn-light/src/config/commandParser.h b/hicn-light/src/config/commandParser.h new file mode 100755 index 000000000..78e19e6e3 --- /dev/null +++ b/hicn-light/src/config/commandParser.h @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file command_Parser.h + * @brief Creates a dictionary of commands and parses a command_line to match + * against them + * + * A user creates individual CommandParserEntry that map a command_line to a + * function to execute. The CommandParser then does a longest-matching prefix + * match of a command_line to the dictionary of commands and executes the + * appropriate command. + * + */ + +#ifndef command_parser_h +#define command_parser_h + +#include +#include + +struct command_parser; +typedef struct command_parser CommandParser; + +/** + * controlState_Create + * + * Creates the global state for the Control program + * + * @return non-null A command parser + * + * Example: + * @code + * <#example#> + * @endcode + */ +CommandParser *commandParser_Create(void); + +/** + * Destroys the control state, closing all network connections + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void commandParser_Destroy(CommandParser **statePtr); + +/** + * Registers a CommandOps with the system. + * + * Each command has its complete command prefix in the "command" field. + * RegisterCommand will put these command prefixes in to a tree and then match + * what a user types against the longest-matching prefix in the tree. If + * there's a match, it will call the "execute" function. + * + * When the parser is destroyed, each command's destroyer function will be + * called. + * + * @param [in] state An allocated ControlState + * @param [in] command The command to register with the system + * + * Example: + * @code + * static ControlReturn + * control_Root_Execute(CommandParser *parser, CommandOps *ops, PARCList + * *args) + * { + * printf("Root Command\n"); + * return CommandReturn_Success; + * } + * + * static ControlReturn + * control_FooBar_Execute(CommandParser *parser, CommandOps *ops, PARCList + * *args) + * { + * printf("Foo Bar Command\n"); + * return CommandReturn_Success; + * } + * + * const CommandOps control_Root = { + * .closure = NULL, + * .command = "", // empty string for root + * .init = NULL, + * .execute = control_Root_Execute + * }; + * + * const CommandOps control_FooBar = { + * .closure = NULL, + * .command = "foo bar", // empty string for root + * .init = NULL, + * .execute = control_FooBar_Execute + * }; + * + * void startup(void) + * { + * ControlState *state = controlState_Create("happy", "day"); + * controlState_RegisterCommand(state, control_FooBar); + * controlState_RegisterCommand(state, control_Root); + * + * // this executes "root" + * controlState_DispatchCommand(state, "foo"); + * controlState_Destroy(&state); + * } + * @endcode + */ +void commandParser_RegisterCommand(CommandParser *state, CommandOps *command); + +/** + * Performs a longest-matching prefix of the args to the command tree + * + * The command tree is created with controlState_RegisterCommand. + * + * @param [in] state The allocated ControlState + * @param [in] args Each command_line word parsed to the ordered list + * + * @return CommandReturn_Success the command was successful + * @return CommandReturn_Failure the command failed or was not found + * @return CommandReturn_Exit the command indicates that the interactive mode + * should exit + * + * Example: + * @code + * <#example#> + * @endcode + */ +CommandReturn commandParser_DispatchCommand(CommandParser *state, + PARCList *args); + +/** + * Sets the Debug mode, which will print out much more information. + * + * Prints out much more diagnostic information about what hicn-light controller + * is doing. yes, you would make a CommandOps to set and unset this :) + * + * @param [in] debugFlag true means to print debug info, false means to turn it + * off + * + * Example: + * @code + * <#example#> + * @endcode + */ +void commandParser_SetDebug(CommandParser *state, bool debugFlag); + +/** + * Returns the debug state of ControlState + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool commandParser_GetDebug(CommandParser *state); + +/** + * Checks if the command is registered + * + * Checks if the exact command given is registered. This is not a prefix match. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return true The command is registered + * @return false The command is not registered + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool commandParser_ContainsCommand(CommandParser *parser, const char *command); +#endif // command_parser_h diff --git a/hicn-light/src/config/commandReturn.h b/hicn-light/src/config/commandReturn.h new file mode 100755 index 000000000..16ee93db1 --- /dev/null +++ b/hicn-light/src/config/commandReturn.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file command_Return.h + * @brief The return code used by CLI commands + * + * This return code is used throughout the command parser and command + * implementations to indicate success, failure, or if the program should exit. + * + */ + +#ifndef command_return_h +#define command_return_h + +/** + * @typedef ControlReturn + * @abstract A command returns one of (SUCCESS, FAILURE, EXIT) + * @constant SUCCESS means the command succeeded + * @constant FAILURE indicates failure + * @constant EXIT means the command indicated that hicn-light controller should + * exit. + * @discussion <#Discussion#> + */ +typedef enum command_return { + CommandReturn_Success, // command returned success + CommandReturn_Failure, // command failure + CommandReturn_Exit // command indicates program should exit +} CommandReturn; + +#endif // command_return_h diff --git a/hicn-light/src/config/configuration.c b/hicn-light/src/config/configuration.c new file mode 100755 index 000000000..cccd60620 --- /dev/null +++ b/hicn-light/src/config/configuration.c @@ -0,0 +1,1076 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * + * Example: + * @code + * <#example#> + * @endcode + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#ifdef WITH_MAPME +#include +#endif /* WITH_MAPME */ + +#include + +#include +#include +#include + +#include +#include //the listener list +#include // needed to print +#include +#include + +#include + +#define ETHERTYPE 0x0801 + +struct configuration { + Forwarder *forwarder; + Logger *logger; + + size_t maximumContentObjectStoreSize; + + // map from prefix (parcString) to strategy (parcString) + PARCHashMap *strategy_map; + + // translates between a symblic name and a connection id + SymbolicNameTable *symbolicNameTable; +}; + +// ======================================================================================== + +Configuration *configuration_Create(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter hicn-fwd must be non-null"); + Configuration *config = parcMemory_AllocateAndClear(sizeof(Configuration)); + parcAssertNotNull(config, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Configuration)); + config->forwarder = forwarder; + config->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + config->maximumContentObjectStoreSize = 100000; + config->strategy_map = parcHashMap_Create(); + config->symbolicNameTable = symbolicNameTable_Create(); + + return config; +} + +void configuration_Destroy(Configuration **configPtr) { + parcAssertNotNull(configPtr, "Parameter must be non-null double poitner"); + parcAssertNotNull(*configPtr, + "Parameter must dereference to non-null pointer"); + + Configuration *config = *configPtr; + logger_Release(&config->logger); + parcHashMap_Release(&(config->strategy_map)); + symbolicNameTable_Destroy(&config->symbolicNameTable); + parcMemory_Deallocate((void **)&config); + *configPtr = NULL; +} + +struct iovec *configuration_ProcessRegisterHIcnPrefix(Configuration *config, + struct iovec *request, + unsigned ingressId) { + header_control_message *header = request[0].iov_base; + add_route_command *control = request[1].iov_base; + + bool success = false; + + const char *symbolicOrConnid = control->symbolicOrConnid; + + if (strcmp(symbolicOrConnid, "SELF_ROUTE") == 0) { + success = forwarder_AddOrUpdateRoute(config->forwarder, control, ingressId); + } else if (utils_IsNumber(symbolicOrConnid)) { + // case for connid as input + unsigned connid = (unsigned)strtold(symbolicOrConnid, NULL); + ConnectionTable *table = forwarder_GetConnectionTable(config->forwarder); + + // check if iconnID present in the fwd table + if (connectionTable_FindById(table, connid)) { + success = forwarder_AddOrUpdateRoute(config->forwarder, control, connid); + } else { + logger_Log(forwarder_GetLogger(config->forwarder), LoggerFacility_IO, + PARCLogLevel_Error, __func__, + "ConnID not found, check list connections"); + // failure + } + + } else { + // case for symbolic as input: check if symbolic name can be resolved + unsigned connid = + symbolicNameTable_Get(config->symbolicNameTable, symbolicOrConnid); + // connid = UINT_MAX when symbolicName is not found + if (connid != UINT32_MAX) { + if (logger_IsLoggable(config->logger, LoggerFacility_Config, + PARCLogLevel_Debug)) { + logger_Log(config->logger, LoggerFacility_Config, PARCLogLevel_Debug, + __func__, "Add route resolve name '%s' to connid %u", + symbolicOrConnid, connid); + } + + success = forwarder_AddOrUpdateRoute(config->forwarder, control, connid); + + } else { + if (logger_IsLoggable(config->logger, LoggerFacility_Config, + PARCLogLevel_Warning)) { + logger_Log(config->logger, LoggerFacility_Config, PARCLogLevel_Warning, + __func__, + "Add route symbolic name '%s' could not be resolved", + symbolicOrConnid); + } + // failure + } + } + + // generate ACK/NACK + struct iovec *response; + + if (success) { // ACK + response = utils_CreateAck(header, control, sizeof(add_route_command)); + } else { // NACK + response = utils_CreateNack(header, control, sizeof(add_route_command)); + } + + return response; +} + +struct iovec *configuration_ProcessUnregisterHIcnPrefix(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + remove_route_command *control = request[1].iov_base; + + bool success = false; + + const char *symbolicOrConnid = control->symbolicOrConnid; + + if (utils_IsNumber(symbolicOrConnid)) { + // case for connid as input + unsigned connid = (unsigned)strtold(symbolicOrConnid, NULL); + ConnectionTable *table = forwarder_GetConnectionTable(config->forwarder); + + // check if interface index present in the fwd table + if (connectionTable_FindById(table, connid)) { + success = forwarder_RemoveRoute(config->forwarder, control, connid); + } else { + logger_Log(forwarder_GetLogger(config->forwarder), LoggerFacility_IO, + PARCLogLevel_Error, __func__, + "ConnID not found, check list connections"); + // failure + } + + } else { + // case for symbolic as input: chech if symbolic name can be resolved + unsigned connid = + symbolicNameTable_Get(config->symbolicNameTable, symbolicOrConnid); + // connid = UINT_MAX when symbolicName is not found + if (connid != UINT32_MAX) { + if (logger_IsLoggable(config->logger, LoggerFacility_Config, + PARCLogLevel_Debug)) { + logger_Log(config->logger, LoggerFacility_Config, PARCLogLevel_Debug, + __func__, "Remove route resolve name '%s' to connid %u", + symbolicOrConnid, connid); + } + success = forwarder_RemoveRoute(config->forwarder, control, connid); + } else { + if (logger_IsLoggable(config->logger, LoggerFacility_Config, + PARCLogLevel_Warning)) { + logger_Log(config->logger, LoggerFacility_Config, PARCLogLevel_Warning, + __func__, + "Remove route symbolic name '%s' could not be resolved", + symbolicOrConnid); + } + // failure + } + } + + // generate ACK/NACK + struct iovec *response; + + if (success) { // ACK + response = utils_CreateAck(header, control, sizeof(remove_route_command)); + } else { // NACK + response = utils_CreateNack(header, control, sizeof(remove_route_command)); + } + + return response; +} + +struct iovec *configuration_ProcessRegistrationList(Configuration *config, + struct iovec *request) { + FibEntryList *fibList = forwarder_GetFibEntries(config->forwarder); + + size_t payloadSize = fibEntryList_Length(fibList); + size_t pointerLocation = 0; + struct sockaddr_in tmpAddr; + struct sockaddr_in6 tmpAddr6; + + // allocate payload, cast from void* to uint8_t* = bytes granularity + uint8_t *payloadResponse = + parcMemory_AllocateAndClear(sizeof(list_routes_command) * payloadSize); + + for (size_t i = 0; i < fibEntryList_Length(fibList); i++) { + FibEntry *entry = (FibEntry *)fibEntryList_Get(fibList, i); + NameBitvector *prefix = name_GetContentName(fibEntry_GetPrefix(entry)); + const NumberSet *nexthops = fibEntry_GetNexthops(entry); + + if (numberSet_Length(nexthops) > 1) { + // payload extended, need reallocate, further entries via nexthops + payloadSize = payloadSize + numberSet_Length(nexthops) - 1; + payloadResponse = (uint8_t *)parcMemory_Reallocate( + payloadResponse, sizeof(list_routes_command) * payloadSize); + } + + for (size_t j = 0; j < numberSet_Length(nexthops); j++) { + list_routes_command *listRouteCommand = + (list_routes_command *)(payloadResponse + + (pointerLocation * + sizeof(list_routes_command))); + + Address *addressEntry = nameBitvector_ToAddress(prefix); + if (addressGetType(addressEntry) == ADDR_INET) { + addressGetInet(addressEntry, &tmpAddr); + listRouteCommand->addressType = ADDR_INET; + listRouteCommand->address.ipv4 = tmpAddr.sin_addr.s_addr; + } else if (addressGetType(addressEntry) == ADDR_INET6) { + addressGetInet6(addressEntry, &tmpAddr6); + listRouteCommand->addressType = ADDR_INET6; + listRouteCommand->address.ipv6 = tmpAddr6.sin6_addr; + } + listRouteCommand->connid = numberSet_GetItem(nexthops, j); + listRouteCommand->len = nameBitvector_GetLength(prefix); + listRouteCommand->cost = 1; // cost + + pointerLocation++; + addressDestroy(&addressEntry); + } + } + + // send response + header_control_message *header = request[0].iov_base; + header->messageType = RESPONSE_LIGHT; + header->length = payloadSize; + + struct iovec *response = + parcMemory_AllocateAndClear(sizeof(struct iovec) * 2); + + response[0].iov_base = header; + response[0].iov_len = sizeof(header_control_message); + response[1].iov_base = payloadResponse; + response[1].iov_len = sizeof(list_routes_command) * payloadSize; + + return response; +} + +static void configuration_SendResponse(Configuration *config, struct iovec *msg, + unsigned egressId) { + ConnectionTable *connectionTable = + forwarder_GetConnectionTable(config->forwarder); + const Connection *conn = connectionTable_FindById(connectionTable, egressId); + parcAssertNotNull(conn, + "Got null connection for control message we just received"); + + IoOperations *ops = connection_GetIoOperations(conn); + streamState_SendCommandResponse(ops, msg); +} + +struct iovec *configuration_ProcessCreateTunnel(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + add_connection_command *control = request[1].iov_base; + + bool success = false; + bool exists = true; + + const char *symbolicName = control->symbolic; + + Address *source = NULL; + Address *destination = NULL; + + if (!symbolicNameTable_Exists(config->symbolicNameTable, symbolicName)) { + if (control->ipType == ADDR_INET) { + source = + utils_AddressFromInet(&control->localIp.ipv4, &control->localPort); + destination = + utils_AddressFromInet(&control->remoteIp.ipv4, &control->remotePort); + } else if (control->ipType == ADDR_INET6) { + source = + utils_AddressFromInet6(&control->localIp.ipv6, &control->localPort); + destination = + utils_AddressFromInet6(&control->remoteIp.ipv6, &control->remotePort); + } else { + printf("Invalid IP type.\n"); // will generate a Nack + } + + AddressPair *pair = addressPair_Create(source, destination); + const Connection *conn = connectionTable_FindByAddressPair( + forwarder_GetConnectionTable(config->forwarder), pair); + + addressPair_Release(&pair); + + if (conn == NULL) { + // the connection does not exists (even without a name) + exists = false; + } + } + + if (!exists) { + IoOperations *ops = NULL; + switch (control->connectionType) { + case TCP_CONN: + // logger_Log(config->logger, LoggerFacility_Config, PARCLogLevel_Error, + // __func__, + // "Unsupported tunnel protocol: TCP"); + ops = tcpTunnel_Create(config->forwarder, source, destination); + break; + case UDP_CONN: + ops = udpTunnel_Create(config->forwarder, source, destination); + break; + case GRE_CONN: + logger_Log(config->logger, LoggerFacility_Config, PARCLogLevel_Error, + __func__, "Unsupported tunnel protocol: GRE"); + break; +#ifndef __APPLE__ + case HICN_CONN: + ops = hicnTunnel_Create(config->forwarder, source, destination); + break; +#endif /* __APPLE__ */ + default: + logger_Log(config->logger, LoggerFacility_Config, PARCLogLevel_Error, + __func__, "Unsupported tunnel protocol: %d", + control->connectionType); + break; + } + + if (ops != NULL) { + Connection *conn = connection_Create(ops); + + connectionTable_Add(forwarder_GetConnectionTable(config->forwarder), + conn); + symbolicNameTable_Add(config->symbolicNameTable, symbolicName, + connection_GetConnectionId(conn)); + + success = true; + + } else { + printf("failed, could not create IoOperations"); + } + + } else { + printf("failed, symbolic name or connextion already exist\n"); + } + + addressDestroy(&source); + addressDestroy(&destination); + + // generate ACK/NACK + struct iovec *response; + + if (success) { // ACK + response = utils_CreateAck(header, control, sizeof(add_connection_command)); + } else { // NACK + response = + utils_CreateNack(header, control, sizeof(add_connection_command)); + } + + return response; +} + +/** + * Add an IP-based tunnel. + * + * The call cal fail if the symbolic name is a duplicate. It could also fail if + * there's an problem creating the local side of the tunnel (i.e. the local + * socket address is not usable). + * + * @return true Tunnel added + * @return false Tunnel not added (an error) + */ + +struct iovec *configuration_ProcessRemoveTunnel(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + remove_connection_command *control = request[1].iov_base; + + bool success = false; + + const char *symbolicOrConnid = control->symbolicOrConnid; + ConnectionTable *table = forwarder_GetConnectionTable(config->forwarder); + + if (utils_IsNumber(symbolicOrConnid)) { + // case for connid as input + unsigned connid = (unsigned)strtold(symbolicOrConnid, NULL); + + // check if interface index present in the fwd table + //(it was missing and therefore caused a program crash) + if (connectionTable_FindById(table, connid)) { + // remove connection from the FIB + forwarder_RemoveConnectionIdFromRoutes(config->forwarder, connid); + // remove connection + connectionTable_RemoveById(table, connid); + + success = true; + } else { + logger_Log(forwarder_GetLogger(config->forwarder), LoggerFacility_IO, + PARCLogLevel_Error, __func__, + "ConnID not found, check list connections"); + // failure + } + + } else { + // case for symbolic as input + // chech if symbolic name can be resolved + unsigned connid = + symbolicNameTable_Get(config->symbolicNameTable, symbolicOrConnid); + // connid = UINT_MAX when symbolicName is not found + if (connid != UINT32_MAX) { + if (logger_IsLoggable(config->logger, LoggerFacility_Config, + PARCLogLevel_Debug)) { + logger_Log(config->logger, LoggerFacility_Config, PARCLogLevel_Debug, + __func__, "Remove connection resolve name '%s' to connid %u", + symbolicOrConnid, connid); + } + + // remove connection from the FIB + forwarder_RemoveConnectionIdFromRoutes(config->forwarder, connid); + // remove connection + connectionTable_RemoveById(table, connid); + // remove connection from symbolicNameTable since we have symbolic input + symbolicNameTable_Remove(config->symbolicNameTable, symbolicOrConnid); + success = true; // to write + } else { + if (logger_IsLoggable(config->logger, LoggerFacility_Config, + PARCLogLevel_Warning)) { + logger_Log(config->logger, LoggerFacility_Config, PARCLogLevel_Error, + __func__, + "Remove connection symbolic name '%s' could not be resolved", + symbolicOrConnid); + } + // failure + } + } + + // generate ACK/NACK + struct iovec *response; + + if (success) { // ACK + response = + utils_CreateAck(header, control, sizeof(remove_connection_command)); + } else { // NACK + response = + utils_CreateNack(header, control, sizeof(remove_connection_command)); + } + + return response; +} + +struct iovec *configuration_ProcessConnectionList(Configuration *config, + struct iovec *request) { + ConnectionTable *table = forwarder_GetConnectionTable(config->forwarder); + ConnectionList *connList = connectionTable_GetEntries(table); + struct sockaddr_in tmpAddr; + struct sockaddr_in6 tmpAddr6; + + // allocate payload, cast from void* to uint8_t* fot bytes granularity + uint8_t *payloadResponse = parcMemory_AllocateAndClear( + sizeof(list_connections_command) * connectionList_Length(connList)); + + for (size_t i = 0; i < connectionList_Length(connList); i++) { + // Don't release original, it is not stored + Connection *original = connectionList_Get(connList, i); + + const AddressPair *addressPair = connection_GetAddressPair(original); + Address *localAddress = addressCopy(addressPair_GetLocal(addressPair)); + Address *remoteAddress = addressCopy(addressPair_GetRemote(addressPair)); + + // Fill payload by shifting and casting at each 'i' step. + list_connections_command *listConnectionsCommand = + (list_connections_command *)(payloadResponse + + (i * sizeof(list_connections_command))); + + // set structure fields + listConnectionsCommand->connid = connection_GetConnectionId(original); + listConnectionsCommand->state = + connection_IsUp(original) ? IFACE_UP : IFACE_DOWN; + listConnectionsCommand->connectionData.connectionType = + ioOperations_GetConnectionType(connection_GetIoOperations(original)); + + if (addressGetType(localAddress) == ADDR_INET && + addressGetType(remoteAddress) == ADDR_INET) { + listConnectionsCommand->connectionData.ipType = ADDR_INET; + + // get local port/address + addressGetInet(localAddress, &tmpAddr); + listConnectionsCommand->connectionData.localPort = tmpAddr.sin_port; + listConnectionsCommand->connectionData.localIp.ipv4 = + tmpAddr.sin_addr.s_addr; + memset(&tmpAddr, 0, sizeof(tmpAddr)); + // get remote port/address + addressGetInet(remoteAddress, &tmpAddr); + listConnectionsCommand->connectionData.remotePort = tmpAddr.sin_port; + listConnectionsCommand->connectionData.remoteIp.ipv4 = + tmpAddr.sin_addr.s_addr; + + } else if (addressGetType(localAddress) == ADDR_INET6 && + addressGetType(remoteAddress) == ADDR_INET6) { + listConnectionsCommand->connectionData.ipType = ADDR_INET6; + + // get local port/address + addressGetInet6(localAddress, &tmpAddr6); + listConnectionsCommand->connectionData.localPort = tmpAddr6.sin6_port; + listConnectionsCommand->connectionData.localIp.ipv6 = tmpAddr6.sin6_addr; + memset(&tmpAddr6, 0, sizeof(tmpAddr6)); + // get remote port/address + addressGetInet6(remoteAddress, &tmpAddr6); + listConnectionsCommand->connectionData.remotePort = tmpAddr6.sin6_port; + listConnectionsCommand->connectionData.remoteIp.ipv6 = tmpAddr6.sin6_addr; + + } // no need further else, control on the addressed already done at the + // time of insertion in the connection table + addressDestroy(&localAddress); + addressDestroy(&remoteAddress); + } + + // send response + header_control_message *header = request[0].iov_base; + header->messageType = RESPONSE_LIGHT; + header->length = (uint16_t)connectionList_Length(connList); + + struct iovec *response = + parcMemory_AllocateAndClear(sizeof(struct iovec) * 2); + + response[0].iov_base = header; + response[0].iov_len = sizeof(header_control_message); + response[1].iov_base = payloadResponse; + response[1].iov_len = + sizeof(list_connections_command) * connectionList_Length(connList); + + return response; +} + +struct iovec *configuration_ProcessListenersList(Configuration *config, + struct iovec *request) { + ListenerSet *listenerList = forwarder_GetListenerSet(config->forwarder); + struct sockaddr_in tmpAddr; + struct sockaddr_in6 tmpAddr6; + + // allocate payload, cast from void* to uint8_t* fot bytes granularity + uint8_t *payloadResponse = parcMemory_AllocateAndClear( + sizeof(list_listeners_command) * listenerSet_Length(listenerList)); + + for (size_t i = 0; i < listenerSet_Length(listenerList); i++) { + ListenerOps *listenerEntry = listenerSet_Get(listenerList, i); + + // Fill payload by shifting and casting at each 'i' step. + list_listeners_command *listListenersCommand = + (list_listeners_command *)(payloadResponse + + (i * sizeof(list_listeners_command))); + + listListenersCommand->connid = + (uint32_t)listenerEntry->getInterfaceIndex(listenerEntry); + listListenersCommand->encapType = + (uint8_t)listenerEntry->getEncapType(listenerEntry); + if (addressGetType((const Address *)listenerEntry->getListenAddress( + listenerEntry)) == ADDR_INET) { + addressGetInet( + (const Address *)listenerEntry->getListenAddress(listenerEntry), + &tmpAddr); + listListenersCommand->addressType = ADDR_INET; + listListenersCommand->address.ipv4 = tmpAddr.sin_addr.s_addr; + listListenersCommand->port = tmpAddr.sin_port; + } else if (addressGetType((const Address *)listenerEntry->getListenAddress( + listenerEntry)) == ADDR_INET6) { + addressGetInet6( + (const Address *)listenerEntry->getListenAddress(listenerEntry), + &tmpAddr6); + listListenersCommand->addressType = ADDR_INET6; + listListenersCommand->address.ipv6 = tmpAddr6.sin6_addr; + listListenersCommand->port = tmpAddr6.sin6_port; + } + } + + // send response + header_control_message *header = request[0].iov_base; + header->messageType = RESPONSE_LIGHT; + header->length = (uint16_t)listenerSet_Length(listenerList); + + struct iovec *response = + parcMemory_AllocateAndClear(sizeof(struct iovec) * 2); + + response[0].iov_base = header; + response[0].iov_len = sizeof(header_control_message); + response[1].iov_base = payloadResponse; + response[1].iov_len = + sizeof(list_listeners_command) * listenerSet_Length(listenerList); + + return response; +} + +struct iovec *configuration_ProcessCacheStore(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + ; + cache_store_command *control = request[1].iov_base; + ; + + bool success = false; + + switch (control->activate) { + case ACTIVATE_ON: + forwarder_SetChacheStoreFlag(config->forwarder, true); + if (forwarder_GetChacheStoreFlag(config->forwarder)) { + success = true; + } + break; + + case ACTIVATE_OFF: + forwarder_SetChacheStoreFlag(config->forwarder, false); + if (!forwarder_GetChacheStoreFlag(config->forwarder)) { + success = true; + } + break; + + default: + break; + } + + struct iovec *response; + if (success) { // ACK + response = utils_CreateAck(header, control, sizeof(cache_store_command)); + } else { // NACK + response = utils_CreateNack(header, control, sizeof(cache_store_command)); + } + + return response; +} + +struct iovec *configuration_ProcessCacheServe(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + cache_serve_command *control = request[1].iov_base; + + bool success = false; + + switch (control->activate) { + case ACTIVATE_ON: + forwarder_SetChacheServeFlag(config->forwarder, true); + if (forwarder_GetChacheServeFlag(config->forwarder)) { + success = true; + } + break; + + case ACTIVATE_OFF: + forwarder_SetChacheServeFlag(config->forwarder, false); + if (!forwarder_GetChacheServeFlag(config->forwarder)) { + success = true; + } + break; + + default: + break; + } + + struct iovec *response; + if (success) { // ACK + response = utils_CreateAck(header, control, sizeof(cache_store_command)); + } else { // NACK + response = utils_CreateNack(header, control, sizeof(cache_store_command)); + } + + return response; +} + +struct iovec *configuration_ProcessCacheClear(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + + forwarder_ClearCache(config->forwarder); + + struct iovec *response = utils_CreateAck(header, NULL, 0); + return response; +} + +size_t configuration_GetObjectStoreSize(Configuration *config) { + return config->maximumContentObjectStoreSize; +} + +void _configuration_StoreFwdStrategy(Configuration *config, const char *prefix, + strategy_type strategy) { + PARCString *prefixStr = parcString_Create(prefix); + PARCUnsigned *strategyValue = parcUnsigned_Create((unsigned)strategy); + parcHashMap_Put(config->strategy_map, prefixStr, strategyValue); + parcUnsigned_Release(&strategyValue); + parcString_Release(&prefixStr); +} + +struct iovec *configuration_SetWldr(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + set_wldr_command *control = request[1].iov_base; + ConnectionTable *table = forwarder_GetConnectionTable(config->forwarder); + Connection *conn = NULL; + bool success = false; + + const char *symbolicOrConnid = control->symbolicOrConnid; + + if (utils_IsNumber(symbolicOrConnid)) { + // case for connid as input: check if connID present in the fwd table + conn = (Connection *)connectionTable_FindById( + table, (unsigned)strtold(symbolicOrConnid, NULL)); + if (conn) { + success = true; + } else { + logger_Log(forwarder_GetLogger(config->forwarder), LoggerFacility_IO, + PARCLogLevel_Error, __func__, + "ConnID not found, check list connections"); // failure + } + } else { + // case for symbolic as input: check if symbolic name can be resolved + unsigned connid = + symbolicNameTable_Get(config->symbolicNameTable, symbolicOrConnid); + if (connid != UINT32_MAX) { + conn = (Connection *)connectionTable_FindById(table, connid); + if (conn) { + if (logger_IsLoggable(config->logger, LoggerFacility_Config, + PARCLogLevel_Debug)) { + logger_Log(config->logger, LoggerFacility_Config, PARCLogLevel_Debug, + __func__, "Set wldr resolve name '%s' to connid %u", + symbolicOrConnid, connid); + } + success = true; + } + } else { + if (logger_IsLoggable(config->logger, LoggerFacility_Config, + PARCLogLevel_Warning)) { + logger_Log(config->logger, LoggerFacility_Config, PARCLogLevel_Error, + __func__, "Symbolic name '%s' could not be resolved", + symbolicOrConnid); + } // failure + } + } + + // generate ACK/NACK + struct iovec *response; + + if (success) { + switch (control->activate) { + case ACTIVATE_ON: + connection_EnableWldr(conn); + response = utils_CreateAck(header, control, sizeof(set_wldr_command)); + break; + + case ACTIVATE_OFF: + connection_DisableWldr(conn); + response = utils_CreateAck(header, control, sizeof(set_wldr_command)); + break; + + default: // received wrong value + response = utils_CreateNack(header, control, sizeof(set_wldr_command)); + break; + } + } else { + response = utils_CreateNack(header, control, sizeof(set_wldr_command)); + } + + return response; +} + +strategy_type configuration_GetForwardingStrategy(Configuration *config, + const char *prefix) { + PARCString *prefixStr = parcString_Create(prefix); + const unsigned *val = parcHashMap_Get(config->strategy_map, prefixStr); + parcString_Release(&prefixStr); + + if (val == NULL) { + return LAST_STRATEGY_VALUE; + } else { + return (strategy_type)*val; + } +} + +struct iovec *configuration_SetForwardingStrategy(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + set_strategy_command *control = request[1].iov_base; + + const char *prefix = utils_PrefixLenToString( + control->addressType, &control->address, &control->len); + strategy_type strategy = control->strategyType; + strategy_type existingFwdStrategy = + configuration_GetForwardingStrategy(config, prefix); + + if (existingFwdStrategy == LAST_STRATEGY_VALUE || + strategy != existingFwdStrategy) { + // means such a new strategy is not present in the hash table or has to be + // updated + _configuration_StoreFwdStrategy(config, prefix, strategy); + Name *hicnPrefix = name_CreateFromAddress(control->addressType, + control->address, control->len); + forwarder_SetStrategy(config->forwarder, hicnPrefix, strategy); + name_Release(&hicnPrefix); + } + + struct iovec *response = + utils_CreateAck(header, control, sizeof(set_strategy_command)); + + return response; +} + +void configuration_SetObjectStoreSize(Configuration *config, + size_t maximumObjectCount) { + config->maximumContentObjectStoreSize = maximumObjectCount; + + forwarder_SetContentObjectStoreSize(config->forwarder, + config->maximumContentObjectStoreSize); +} + +Forwarder *configuration_GetForwarder(const Configuration *config) { + return config->forwarder; +} + +Logger *configuration_GetLogger(const Configuration *config) { + return config->logger; +} + +struct iovec *configuration_MapMeEnable(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + mapme_activator_command *control = request[1].iov_base; + const char *stateString[2] = {"on", "off"}; + + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcBufferComposer_Format(composer, + "The mapme enable setting received is: %s", + stateString[control->activate]); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + puts(result); + parcMemory_Deallocate((void **)&result); + parcBufferComposer_Release(&composer); + + struct iovec *response = + utils_CreateAck(header, control, sizeof(mapme_activator_command)); + + return response; +} + +struct iovec *configuration_MapMeDiscovery(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + mapme_activator_command *control = request[1].iov_base; + const char *stateString[2] = {"on", "off"}; + + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcBufferComposer_Format(composer, + "The mapme discovery setting received is: %s", + stateString[control->activate]); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + puts(result); + parcMemory_Deallocate((void **)&result); + parcBufferComposer_Release(&composer); + + struct iovec *response = + utils_CreateAck(header, control, sizeof(mapme_activator_command)); + + return response; +} + +struct iovec *configuration_MapMeTimescale(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + mapme_timing_command *control = request[1].iov_base; + + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcBufferComposer_Format(composer, + "The mapme timescale value received is: %u", + control->timePeriod); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + puts(result); + parcMemory_Deallocate((void **)&result); + parcBufferComposer_Release(&composer); + + struct iovec *response = + utils_CreateAck(header, control, sizeof(mapme_timing_command)); + + return response; +} + +struct iovec *configuration_MapMeRetx(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + mapme_timing_command *control = request[1].iov_base; + + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcBufferComposer_Format( + composer, "The mapme retransmission time value received is: %u", + control->timePeriod); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + puts(result); + parcMemory_Deallocate((void **)&result); + parcBufferComposer_Release(&composer); + + struct iovec *response = + utils_CreateAck(header, control, sizeof(mapme_timing_command)); + + return response; +} + +// =========================== +// Main functions that deal with receiving commands, executing them, and sending +// ACK/NACK + +struct iovec *configuration_DispatchCommand(Configuration *config, + command_id command, + struct iovec *control, + unsigned ingressId) { + struct iovec *response = NULL; + + switch (command) { + case ADD_LISTENER: + response = configurationListeners_Add(config, control, ingressId); + break; + + case ADD_CONNECTION: + response = configuration_ProcessCreateTunnel(config, control); + break; + + case LIST_CONNECTIONS: + response = configuration_ProcessConnectionList(config, control); + break; + + case ADD_ROUTE: + response = + configuration_ProcessRegisterHIcnPrefix(config, control, ingressId); + break; + + case LIST_ROUTES: + response = configuration_ProcessRegistrationList(config, control); + break; + + case REMOVE_CONNECTION: + response = configuration_ProcessRemoveTunnel(config, control); + break; + + case REMOVE_ROUTE: + response = configuration_ProcessUnregisterHIcnPrefix(config, control); + break; + + case CACHE_STORE: + response = configuration_ProcessCacheStore(config, control); + break; + + case CACHE_SERVE: + response = configuration_ProcessCacheServe(config, control); + break; + + case CACHE_CLEAR: + response = configuration_ProcessCacheClear(config, control); + break; + + case SET_STRATEGY: + response = configuration_SetForwardingStrategy(config, control); + break; + + case SET_WLDR: + response = configuration_SetWldr(config, control); + break; + + case ADD_PUNTING: + response = configurationListeners_AddPunting(config, control, ingressId); + break; + + case LIST_LISTENERS: + response = configuration_ProcessListenersList(config, control); + break; + + case MAPME_ENABLE: + response = configuration_MapMeEnable(config, control); + break; + + case MAPME_DISCOVERY: + response = configuration_MapMeDiscovery(config, control); + break; + + case MAPME_TIMESCALE: + response = configuration_MapMeTimescale(config, control); + break; + + case MAPME_RETX: + response = configuration_MapMeRetx(config, control); + break; + + default: + break; + } + + return response; +} + +void configuration_ReceiveCommand(Configuration *config, command_id command, + struct iovec *request, unsigned ingressId) { + parcAssertNotNull(config, "Parameter config must be non-null"); + parcAssertNotNull(request, "Parameter request must be non-null"); + struct iovec *response = + configuration_DispatchCommand(config, command, request, ingressId); + configuration_SendResponse(config, response, ingressId); + + switch (command) { + case LIST_CONNECTIONS: + case LIST_ROUTES: // case LIST_INTERFACES: case ETC...: + parcMemory_Deallocate( + &response[1] + .iov_base); // deallocate payload only if generated at fwd side + break; + default: + break; + } + + // deallocate received request. It coincides with response[0].iov_base memory + // parcMemory_Deallocate(&request); //deallocate header and payload (if + // same sent by controller) + parcMemory_Deallocate(&response); // deallocate iovec pointer +} diff --git a/hicn-light/src/config/configuration.h b/hicn-light/src/config/configuration.h new file mode 100755 index 000000000..2bf66c0b1 --- /dev/null +++ b/hicn-light/src/config/configuration.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file configuration.h + * @brief hicn-light configuration, such as in-band commands or CLI + * + * Manages all user configuration of the system, such as from the CLI or web + * interface It remembers the user commands and will be able to write out a + * config file. + * + */ + +#ifndef configuration_h +#define configuration_h + +#include +#include + +struct configuration; +typedef struct configuration Configuration; + +struct forwarder; +typedef struct forwarder Forwarder; + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @retval <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +Configuration *configuration_Create(Forwarder *forwarder); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @retval <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void configuration_Destroy(Configuration **configPtr); + +void configuration_SetupAllListeners(Configuration *config, uint16_t port, + const char *localPath); + +void configuration_ReceiveCommand(Configuration *config, command_id command, + struct iovec *request, unsigned ingressId); + +/** + * Returns the configured size of the content store + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @retval <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t configuration_GetObjectStoreSize(Configuration *config); + +/** + * Sets the size of the content store (in objects, not bytes) + * + * Must be set before starting the forwarder + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void configuration_SetObjectStoreSize(Configuration *config, + size_t maximumContentObjectCount); + +strategy_type configuration_GetForwardingStrategy(Configuration *config, + const char *prefix); + +/** + * Returns the Forwarder that owns the Configuration + * + * Returns the hicn-light Forwarder. Used primarily by associated classes in + * the configuration group. + * + * @param [in] config An allocated Configuration + * + * @return non-null The owning Forwarder + * @return null An error + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +Forwarder *configuration_GetForwarder(const Configuration *config); + +/** + * Returns the logger used by the Configuration subsystem + * + * Returns the logger specified when the Configuration was created. + * + * @param [in] config An allocated Configuration + * + * @retval non-null The logger + * @retval null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +Logger *configuration_GetLogger(const Configuration *config); + +struct iovec *configuration_DispatchCommand(Configuration *config, + command_id command, + struct iovec *control, + unsigned ingressId); + +#endif // configuration_h diff --git a/hicn-light/src/config/configurationFile.c b/hicn-light/src/config/configurationFile.c new file mode 100755 index 000000000..eab8f9362 --- /dev/null +++ b/hicn-light/src/config/configurationFile.c @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct configuration_file { + Forwarder *forwarder; + const char *filename; + FILE *fh; + + size_t linesRead; + + // our custom state machine. + ControlState *controlState; +}; + +/* + * Called by a command to dispatch the correct command + */ +struct iovec *_writeRead(ControlState *state, struct iovec *msg) { + ConfigurationFile *configFile = + (ConfigurationFile *)controlState_GetUserdata(state); + + parcAssertNotNull(msg, "Parameter msg must be non-null"); + struct iovec *response = configuration_DispatchCommand( + forwarder_GetConfiguration(configFile->forwarder), + ((header_control_message *)msg[0].iov_base)->commandID, msg, 0); + + return response; +} + +/** + * Removes leading whitespace (space + tab). + * + * If the string is all whitespace, the return value will point to the + * terminating '\0'. + * + * @param [in] str A null-terminated c-string + * + * @retval non-null A pointer in to string of the first non-whitespace + * + * + * Example: + * @code + * <#example#> + * @endcode + */ +static char *_stripLeadingWhitespace(char *str) { + while (isspace(*str)) { + str++; + } + return str; +} + +/** + * Removes trailing whitespace + * + * Inserts a NULL after the last non-whitespace character, modiyfing the input + * string. + * + * @param [in] str A null-terminated c-string + * + * @return non-null A pointer to the input string + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +static char *_stripTrailingWhitespace(char *str) { + char *p = str + strlen(str) - 1; + while (p > str && isspace(*p)) { + p--; + } + + // cap it. If no whitespace, p+1 == str + strlen(str), so will overwrite the + // current null. If all whitespace p+1 == str+1. For an empty string, p+1 = + // str. + *(p + 1) = 0; + + // this does not catch the case where the entire string is whitespace + if (p == str && isspace(*p)) { + *p = 0; + } + + return str; +} + +/** + * Removed leading and trailing whitespace + * + * Modifies the input string (may add a NULL at the end). Will return + * a pointer to the first non-whitespace character or the terminating NULL. + * + * @param [in] str A null-terminated c-string + * + * @return non-null A pointer in to the input string + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +static char *_trim(char *str) { + return _stripTrailingWhitespace(_stripLeadingWhitespace(str)); +} + +/** + * Parse a string in to a PARCList with one word per element + * + * The string passed will be modified by inserting NULLs after each token. + * + * @param [in] str A c-string (will be modified) + * + * @retval non-null A PARCList where each item is a single word + * + * Example: + * @code + * <#example#> + * @endcode + */ +static PARCList *_parseArgs(char *str) { + PARCList *list = + parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList); + + const char delimiters[] = " \t"; + + char *token; + while ((token = strsep(&str, delimiters)) != NULL) { + parcList_Add(list, token); + } + + return list; +} + +// ============================================================= + +static void _destroy(ConfigurationFile **configFilePtr) { + ConfigurationFile *configFile = *configFilePtr; + parcMemory_Deallocate((void **)&configFile->filename); + + if (configFile->fh != NULL) { + fclose(configFile->fh); + } + + controlState_Destroy(&configFile->controlState); +} + +parcObject_ExtendPARCObject(ConfigurationFile, _destroy, NULL, NULL, NULL, NULL, + NULL, NULL); + +parcObject_ImplementRelease(configurationFile, ConfigurationFile); + +ConfigurationFile *configurationFile_Create(Forwarder *forwarder, + const char *filename) { + parcAssertNotNull(forwarder, "Parameter hicn-fwd must be non-null"); + parcAssertNotNull(filename, "Parameter filename must be non-null"); + + ConfigurationFile *configFile = parcObject_CreateInstance(ConfigurationFile); + + if (configFile) { + configFile->linesRead = 0; + configFile->forwarder = forwarder; + configFile->filename = + parcMemory_StringDuplicate(filename, strlen(filename)); + parcAssertNotNull(configFile->filename, "Could not copy string '%s'", + filename); + + // setup the control state for the command parser: last parameter NULL + // because + // writeRead still not implemented from configuration file. + configFile->controlState = + controlState_Create(configFile, _writeRead, false); + + // we do not register Help commands + controlState_RegisterCommand(configFile->controlState, + controlRoot_Create(configFile->controlState)); + + // open the file and make sure we can read it + configFile->fh = fopen(configFile->filename, "r"); + + if (configFile->fh) { + if (logger_IsLoggable(forwarder_GetLogger(forwarder), + LoggerFacility_Config, PARCLogLevel_Debug)) { + logger_Log(forwarder_GetLogger(forwarder), LoggerFacility_Config, + PARCLogLevel_Debug, __func__, "Open config file %s", + configFile->filename); + } + } else { + if (logger_IsLoggable(forwarder_GetLogger(forwarder), + LoggerFacility_Config, PARCLogLevel_Error)) { + logger_Log(forwarder_GetLogger(forwarder), LoggerFacility_Config, + PARCLogLevel_Error, __func__, + "Could not open config file %s: (%d) %s", + configFile->filename, errno, strerror(errno)); + } + + // failure cleanup the object -- this nulls it so final return null be + // NULL + configurationFile_Release(&configFile); + } + } + return configFile; +} + +bool configurationFile_Process(ConfigurationFile *configFile) { + parcAssertNotNull(configFile, "Parameter configFile must be non-null"); + + // default to a "true" return value and only set to false if we encounter an + // error. + bool success = true; + +#define BUFFERLEN 2048 + char buffer[BUFFERLEN]; + + configFile->linesRead = 0; + + // always clear errors and fseek to start of file in case we get called + // multiple times. + clearerr(configFile->fh); + rewind(configFile->fh); + + while (success && fgets(buffer, BUFFERLEN, configFile->fh) != NULL) { + configFile->linesRead++; + + char *stripedBuffer = _trim(buffer); + if (strlen(stripedBuffer) > 0) { + if (stripedBuffer[0] != '#') { + // not empty and not a comment + + // _parseArgs will modify the string + char *copy = + parcMemory_StringDuplicate(stripedBuffer, strlen(stripedBuffer)); + PARCList *args = _parseArgs(copy); + CommandReturn result = + controlState_DispatchCommand(configFile->controlState, args); + + // we ignore EXIT from the configuration file + if (result == CommandReturn_Failure) { + if (logger_IsLoggable(forwarder_GetLogger(configFile->forwarder), + LoggerFacility_Config, PARCLogLevel_Error)) { + logger_Log(forwarder_GetLogger(configFile->forwarder), + LoggerFacility_Config, PARCLogLevel_Error, __func__, + "Error on input file %s line %d: %s", + configFile->filename, configFile->linesRead, + stripedBuffer); + } + success = false; + } + parcList_Release(&args); + parcMemory_Deallocate((void **)©); + } + } + } + + if (ferror(configFile->fh)) { + if (logger_IsLoggable(forwarder_GetLogger(configFile->forwarder), + LoggerFacility_Config, PARCLogLevel_Error)) { + logger_Log(forwarder_GetLogger(configFile->forwarder), + LoggerFacility_Config, PARCLogLevel_Error, __func__, + "Error on input file %s line %d: (%d) %s", + configFile->filename, configFile->linesRead, errno, + strerror(errno)); + } + success = false; + } + + return success; +} diff --git a/hicn-light/src/config/configurationFile.h b/hicn-light/src/config/configurationFile.h new file mode 100755 index 000000000..54548191d --- /dev/null +++ b/hicn-light/src/config/configurationFile.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file configurationFile.h + * @brief Accepts a filename and provides a means to read it into Configuration + * + * Reads a configuration file and converts the lines in to configuration + * commands for use in Configuration. + * + * Accepts '#' lines as comments. Skips blank and whitespace-only lines. + * + */ + +#ifndef configurationFile_h +#define configurationFile_h + +#include + +struct configuration_file; +typedef struct configuration_file ConfigurationFile; + +/** + * Creates a ConfigurationFile to prepare to process the file + * + * Prepares the object and opens the file. Makes sure we can read the file. + * Does not read the file or process any commands from the file. + * + * @param [in] hicn-light An allocated Forwarder to configure with the file + * @param [in] filename The file to use + * + * @retval non-null An allocated ConfigurationFile that is readable + * @retval null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +ConfigurationFile *configurationFile_Create(Forwarder *forwarder, + const char *filename); + +/** + * Reads the configuration file line-by-line and issues commands to + * Configuration + * + * Reads the file line by line. Skips '#' and blank lines. + * + * Will stop on the first error. Lines already processed will not be un-done. + * + * @param [in] configFile An allocated ConfigurationFile + * + * @retval true The entire files was processed without error. + * @retval false There was an error in the file. + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool configurationFile_Process(ConfigurationFile *configFile); + +// void configurationFile_ProcessForwardingStrategies(Configuration * config, +// ConfigurationFile * configFile); + +/** + * Closes the underlying file and releases memory + * + * <#Paragraphs Of Explanation#> + * + * @param [in,out] configFilePtr An allocated ConfigurationFile that will be + * NULL'd as output + * + * Example: + * @code + * <#example#> + * @endcode + */ +void configurationFile_Release(ConfigurationFile **configFilePtr); + +#endif /* defined(configurationFile_h) */ diff --git a/hicn-light/src/config/configurationListeners.c b/hicn-light/src/config/configurationListeners.c new file mode 100755 index 000000000..be30e2a49 --- /dev/null +++ b/hicn-light/src/config/configurationListeners.c @@ -0,0 +1,535 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +static bool _setupHIcnListenerOnInet4(Forwarder *forwarder, + const char *symbolic, Address *address) { + bool success = false; +#ifndef __APPLE__ + ListenerOps *ops = + hicnListener_CreateInet(forwarder, (char *)symbolic, address); + if (ops != NULL) { + success = listenerSet_Add(forwarder_GetListenerSet(forwarder), ops); + parcAssertTrue(success, "Failed to add HIcn listener %s to ListenerSet", + symbolic); + } +#endif /* __APPLE__ */ + return success; +} + +static bool _setupHIcnListenerOnInet6(Forwarder *forwarder, + const char *symbolic, Address *address) { + bool success = false; +#ifndef __APPLE__ + ListenerOps *ops = + hicnListener_CreateInet6(forwarder, (char *)symbolic, address); + if (ops != NULL) { + success = listenerSet_Add(forwarder_GetListenerSet(forwarder), ops); + parcAssertTrue(success, "Failed to add HIcn listener %s to ListenerSet", + symbolic); + } +#endif /* __APPLE__ */ + return success; +} + +bool configurationListeners_Remove(const Configuration *config) { + Logger *logger = configuration_GetLogger(config); + if (logger_IsLoggable(logger, LoggerFacility_Config, PARCLogLevel_Warning)) { + logger_Log(logger, LoggerFacility_Config, PARCLogLevel_Warning, __func__, + "Removing a listener not supported: ingress %u control %s"); + } + + return false; +} + +bool _AddPuntingInet(const Configuration *config, Punting *punting, + unsigned ingressId) { +#ifndef __APPLE__ + struct sockaddr *addr = parcNetwork_SockAddress("0.0.0.0", 1234); + if (addr == NULL) { + printf("Error creating address\n"); + return false; + } + + Address *fakeAddr = addressCreateFromInet((struct sockaddr_in *)addr); + + ListenerOps *listenerOps = listenerSet_Find( + forwarder_GetListenerSet(configuration_GetForwarder(config)), ENCAP_HICN, + fakeAddr); + addressDestroy(&fakeAddr); + + if (listenerOps == NULL) { + printf("the main listener (IPV4) does not exists\n"); + return false; + } + + struct sockaddr_in puntingAddr; + + Address *address = puntingGetAddress(punting); + if (address == NULL) return false; + + bool res = addressGetInet(address, &puntingAddr); + if (!res) { + printf("unable to read the punting address\n"); + return false; + } + + char prefix[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &(puntingAddr.sin_addr), prefix, INET_ADDRSTRLEN); + + char len[5]; + sprintf(len, "%d", puntingPrefixLen(punting)); + + char *prefixStr = + malloc(strlen(prefix) + strlen(len) + 2); //+1 for the zero-terminator + if (prefixStr == NULL) { + printf("error while create the prefix string\n"); + return false; + } + strcpy(prefixStr, prefix); + strcat(prefixStr, "/"); + strcat(prefixStr, len); + + res = hicnListener_Punting(listenerOps, prefixStr); + if (!res) { + printf("error while adding the punting rule\n"); + return false; + } + + return true; +#else + return false; +#endif +} + +bool _AddPuntingInet6(const Configuration *config, Punting *punting, + unsigned ingressId) { +#ifndef __APPLE__ + struct sockaddr *addr = parcNetwork_SockAddress("0::0", 1234); + if (addr == NULL) { + printf("Error creating address\n"); + return false; + } + + Address *fakeAddr = addressCreateFromInet6((struct sockaddr_in6 *)addr); + + // comments: + // EncapType: I use the HIcn encap since the puting is available only for HIcn + // listeners LocalAddress: The only listern for which we need punting rules is + // the main one, which has no address + // so I create a fake empty address. This need to be consistent + // with the address set at creation time + + ListenerOps *listenerOps = listenerSet_Find( + forwarder_GetListenerSet(configuration_GetForwarder(config)), ENCAP_HICN, + fakeAddr); + addressDestroy(&fakeAddr); + + if (listenerOps == NULL) { + printf("the main listener does not exists\n"); + return false; + } + + struct sockaddr_in6 puntingAddr; + bool res = addressGetInet6(puntingGetAddress(punting), &puntingAddr); + if (!res) { + printf("unable to read the punting address\n"); + return false; + } + + char prefix[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &(puntingAddr.sin6_addr), prefix, INET6_ADDRSTRLEN); + + char len[5]; + sprintf(len, "%d", puntingPrefixLen(punting)); + + char *prefixStr = + malloc(strlen(prefix) + strlen(len) + 2); //+1 for the zero-terminator + if (prefixStr == NULL) { + printf("error while create the prefix string\n"); + return false; + } + strcpy(prefixStr, prefix); + strcat(prefixStr, "/"); + strcat(prefixStr, len); + + res = hicnListener_Punting(listenerOps, prefixStr); + if (!res) { + printf("error while adding the punting rule\n"); + return false; + } + + return true; +#else + return false; +#endif +} + +//============= LIGHT COMMAN =============== + +static bool _addEther(Configuration *config, add_listener_command *control, + unsigned ingressId) { + // Not implemented + return false; +} + +static bool _setupTcpListenerOnInet(Forwarder *forwarder, ipv4_addr_t *addr4, + uint16_t *port) { + bool success = false; + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = *port; + addr.sin_addr.s_addr = *addr4; + + ListenerOps *ops = tcpListener_CreateInet(forwarder, addr); + if (ops) { + success = listenerSet_Add(forwarder_GetListenerSet(forwarder), ops); + parcAssertTrue(success, "Failed to add TCP listener on %s to ListenerSet", + addressToString(ops->getListenAddress(ops))); + } + return success; +} + +static bool _setupUdpListenerOnInet(Forwarder *forwarder, ipv4_addr_t *addr4, + uint16_t *port) { + bool success = false; + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = *port; + addr.sin_addr.s_addr = *addr4; + + ListenerOps *ops = udpListener_CreateInet(forwarder, addr); + if (ops) { + success = listenerSet_Add(forwarder_GetListenerSet(forwarder), ops); + parcAssertTrue(success, "Failed to add UDP listener on %s to ListenerSet", + addressToString(ops->getListenAddress(ops))); + } + return success; +} + +static bool _setupTcpListenerOnInet6Light(Forwarder *forwarder, + ipv6_addr_t *addr6, uint16_t *port, + uint32_t scopeId) { + bool success = false; + + struct sockaddr_in6 addr; + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_port = *port; + addr.sin6_addr = *addr6; + addr.sin6_scope_id = scopeId; + + ListenerOps *ops = tcpListener_CreateInet6(forwarder, addr); + if (ops) { + success = listenerSet_Add(forwarder_GetListenerSet(forwarder), ops); + parcAssertTrue(success, "Failed to add TCP6 listener on %s to ListenerSet", + addressToString(ops->getListenAddress(ops))); + } + return success; +} + +static bool _setupUdpListenerOnInet6Light(Forwarder *forwarder, + ipv6_addr_t *addr6, uint16_t *port) { + bool success = false; + + struct sockaddr_in6 addr; + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_port = *port; + addr.sin6_addr = *addr6; + addr.sin6_scope_id = 0; + + ListenerOps *ops = udpListener_CreateInet6(forwarder, addr); + if (ops) { + success = listenerSet_Add(forwarder_GetListenerSet(forwarder), ops); + parcAssertTrue(success, "Failed to add UDP6 listener on %s to ListenerSet", + addressToString(ops->getListenAddress(ops))); + } + return success; +} + +bool _addHicn(Configuration *config, add_listener_command *control, + unsigned ingressId) { + bool success = false; + const char *symbolic = control->symbolic; + Address *localAddress = NULL; + + switch (control->addressType) { + case ADDR_INET: { + localAddress = + utils_AddressFromInet(&control->address.ipv4, &control->port); + success = _setupHIcnListenerOnInet4(configuration_GetForwarder(config), + symbolic, localAddress); + break; + } + + case ADDR_INET6: { + localAddress = + utils_AddressFromInet6(&control->address.ipv6, &control->port); + success = _setupHIcnListenerOnInet6(configuration_GetForwarder(config), + symbolic, localAddress); + break; + } + + default: + if (logger_IsLoggable(configuration_GetLogger(config), + LoggerFacility_Config, PARCLogLevel_Warning)) { + logger_Log(configuration_GetLogger(config), LoggerFacility_Config, + PARCLogLevel_Warning, __func__, + "Unsupported address type for HICN (ingress id %u): " + "must be either IPV4 or IPV6", + ingressId); + } + break; + } + + if (success == true && localAddress != NULL) { + if (logger_IsLoggable(configuration_GetLogger(config), + LoggerFacility_Config, PARCLogLevel_Info)) { + logger_Log(configuration_GetLogger(config), LoggerFacility_Config, + PARCLogLevel_Info, __func__, + "Setup hicn listener on address %s", + addressToString(localAddress)); + } + } + + addressDestroy(&localAddress); + + return success; +} + +bool _addIP(Configuration *config, add_listener_command *control, + unsigned ingressId) { + bool success = false; + + switch (control->addressType) { + case ADDR_INET: { + if (control->connectionType == UDP_CONN) { + success = + _setupUdpListenerOnInet(configuration_GetForwarder(config), + &control->address.ipv4, &control->port); + } else if (control->connectionType == TCP_CONN) { + success = + _setupTcpListenerOnInet(configuration_GetForwarder(config), + &control->address.ipv4, &control->port); + } + break; + } + + case ADDR_INET6: { + if (control->connectionType == UDP_CONN) { + success = _setupUdpListenerOnInet6Light( + configuration_GetForwarder(config), &control->address.ipv6, + &control->port); + } else if (control->connectionType == TCP_CONN) { + success = _setupTcpListenerOnInet6Light( + configuration_GetForwarder(config), &control->address.ipv6, + &control->port, 0); + } + break; + } + + default: + if (logger_IsLoggable(configuration_GetLogger(config), + LoggerFacility_Config, PARCLogLevel_Warning)) { + char *addrStr = utils_CommandAddressToString( + control->addressType, &control->address, &control->port); + logger_Log( + configuration_GetLogger(config), LoggerFacility_Config, + PARCLogLevel_Warning, __func__, + "Unsupported address type for IP encapsulation ingress id %u: %s", + ingressId, addrStr); + parcMemory_Deallocate((void **)&addrStr); + } + break; + } + + if (success) { + if (logger_IsLoggable(configuration_GetLogger(config), + LoggerFacility_Config, PARCLogLevel_Info)) { + char *addrStr = utils_CommandAddressToString( + control->addressType, &control->address, &control->port); + logger_Log(configuration_GetLogger(config), LoggerFacility_Config, + PARCLogLevel_Info, __func__, "Setup listener on address %s", + addrStr); + parcMemory_Deallocate((void **)&addrStr); + } + } + + return success; +} + +struct iovec *configurationListeners_Add(Configuration *config, + struct iovec *request, + unsigned ingressId) { + header_control_message *header = request[0].iov_base; + add_listener_command *control = request[1].iov_base; + + bool success = false; + + if (control->listenerMode == ETHER_MODE) { + parcTrapNotImplemented("Add Ethernet Listener is not supported"); + success = _addEther(config, control, ingressId); + // it is a failure + } else if (control->listenerMode == IP_MODE) { + success = _addIP(config, control, ingressId); + } else if (control->listenerMode == HICN_MODE) { + success = _addHicn(config, control, ingressId); + } else { + Logger *logger = configuration_GetLogger(config); + if (logger_IsLoggable(logger, LoggerFacility_Config, + PARCLogLevel_Warning)) { + logger_Log(logger, LoggerFacility_Config, PARCLogLevel_Warning, __func__, + "Unsupported encapsulation mode (ingress id %u)", ingressId); + } + } + + // generate ACK/NACK + struct iovec *response; + + if (success) { // ACK + response = utils_CreateAck(header, control, sizeof(add_listener_command)); + } else { // NACK + response = utils_CreateNack(header, control, sizeof(add_listener_command)); + } + + return response; +} + +struct iovec *configurationListeners_AddPunting(Configuration *config, + struct iovec *request, + unsigned ingressId) { + header_control_message *header = request[0].iov_base; + add_punting_command *control = request[1].iov_base; + + const char *symbolicOrConnid = control->symbolicOrConnid; + uint32_t len = control->len; + in_port_t port = htons(1234); + bool success = false; + + if (control->addressType == ADDR_INET) { + Address *address = utils_AddressFromInet(&control->address.ipv4, &port); + Punting *punting = puntingCreate(symbolicOrConnid, address, len); + success = _AddPuntingInet(config, punting, ingressId); + addressDestroy(&address); + } else if (control->addressType == ADDR_INET6) { + Address *address = utils_AddressFromInet6(&control->address.ipv6, &port); + Punting *punting = puntingCreate(symbolicOrConnid, address, len); + success = _AddPuntingInet6(config, punting, ingressId); + addressDestroy(&address); + } else { + printf("Invalid IP type.\n"); // will generate a Nack + return utils_CreateNack(header, control, sizeof(add_punting_command)); + } + + // generate ACK/NACK + struct iovec *response; + if (success) { // ACK + response = utils_CreateAck(header, control, sizeof(add_punting_command)); + } else { // NACK + response = utils_CreateNack(header, control, sizeof(add_punting_command)); + } + + return response; +} + +//=========================== INITIAL LISTENERS ==================== + +static void _setupListenersOnAddress(Forwarder *forwarder, + const Address *address, uint16_t port, + const char *interfaceName) { + address_type type = addressGetType(address); + switch (type) { + case ADDR_INET: { + struct sockaddr_in tmp; + addressGetInet(address, &tmp); + _setupTcpListenerOnInet(forwarder, &tmp.sin_addr.s_addr, &port); + break; + } + + case ADDR_INET6: { + struct sockaddr_in6 tmp; + addressGetInet6(address, &tmp); + _setupTcpListenerOnInet6Light(forwarder, &tmp.sin6_addr, &port, + tmp.sin6_scope_id); + break; + } + + case ADDR_LINK: + // not used + break; + + default: + // dont' know how to handle this, so no listeners + break; + } +} + +void configurationListeners_SetupAll(const Configuration *config, uint16_t port, + const char *localPath) { + Forwarder *forwarder = configuration_GetForwarder(config); + InterfaceSet *set = system_Interfaces(forwarder); + + size_t interfaceSetLen = interfaceSetLength(set); + for (size_t i = 0; i < interfaceSetLen; i++) { + Interface *iface = interfaceSetGetByOrdinalIndex(set, i); + + const AddressList *addresses = interfaceGetAddresses(iface); + size_t addressListLen = addressListLength(addresses); + + for (size_t j = 0; j < addressListLen; j++) { + const Address *address = addressListGetItem(addresses, j); + + // Do not start on link address + if (addressGetType(address) != ADDR_LINK) { + _setupListenersOnAddress(forwarder, address, htons(port), + interfaceGetName(iface)); + } + } + } + + // if (localPath != NULL) { + // _setupLocalListener(forwarder, localPath); + //} + + interfaceSetDestroy(&set); +} diff --git a/hicn-light/src/config/configurationListeners.h b/hicn-light/src/config/configurationListeners.h new file mode 100755 index 000000000..7332b0c64 --- /dev/null +++ b/hicn-light/src/config/configurationListeners.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file configurationListeners.h + * @brief Configuration routines related to Listeners + * + * Adding and removing listeners. + * + */ + +#ifndef configurationListeners_h +#define configurationListeners_h + +#include +#include + +#include + +/** + * Setup udp, tcp, and local listeners + * + * Will bind to all available IP protocols on the given port. + * Does not add Ethernet listeners. + * + * @param port is the UPD and TCP port to use + * @param localPath is the AF_UNIX path to use, if NULL no AF_UNIX listener is + * setup + * + * Example: + * @code + * <#example#> + * @endcode + */ +void configurationListeners_SetupAll(const Configuration *config, uint16_t port, + const char *localPath); + +bool configurationListeners_Remove(const Configuration *config); + +// light functions + +struct iovec *configurationListeners_Add(Configuration *config, + struct iovec *request, + unsigned ingressId); + +struct iovec *configurationListeners_AddPunting(Configuration *config, + struct iovec *request, + unsigned ingressId); + +#endif /* defined(configurationListeners_h) */ diff --git a/hicn-light/src/config/controlAdd.c b/hicn-light/src/config/controlAdd.c new file mode 100755 index 000000000..72f8e9759 --- /dev/null +++ b/hicn-light/src/config/controlAdd.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +// =================================================== + +static void _controlAdd_Init(CommandParser *parser, CommandOps *ops); +static CommandReturn _controlAdd_Execute(CommandParser *parser, CommandOps *ops, + PARCList *args); +static CommandReturn _controlAdd_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args); + +// =================================================== + +static const char *command_add = "add"; +static const char *help_command_add = "help add"; + +CommandOps *webControlAdd_Create(ControlState *state) { + return commandOps_Create(state, command_add, _controlAdd_Init, + _controlAdd_Execute, commandOps_Destroy); +} + +CommandOps *controlAdd_CreateHelp(ControlState *state) { + return commandOps_Create(state, help_command_add, NULL, + _controlAdd_HelpExecute, commandOps_Destroy); +} + +// =================================================== + +static CommandReturn _controlAdd_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + CommandOps *ops_add_connection = controlAddConnection_Create(NULL); + CommandOps *ops_add_route = controlAddRoute_Create(NULL); + CommandOps *ops_add_punting = controlAddPunting_Create(NULL); + CommandOps *ops_add_listener = controlAddListener_Create(NULL); + + printf("Available commands:\n"); + printf(" %s\n", ops_add_connection->command); + printf(" %s\n", ops_add_route->command); + printf(" %s\n", ops_add_punting->command); + printf(" %s\n", ops_add_listener->command); + printf("\n"); + + commandOps_Destroy(&ops_add_connection); + commandOps_Destroy(&ops_add_route); + commandOps_Destroy(&ops_add_punting); + commandOps_Destroy(&ops_add_listener); + return CommandReturn_Success; +} + +static void _controlAdd_Init(CommandParser *parser, CommandOps *ops) { + ControlState *state = ops->closure; + controlState_RegisterCommand(state, controlAddListener_HelpCreate(state)); + controlState_RegisterCommand(state, controlAddListener_Create(state)); + controlState_RegisterCommand(state, controlAddConnection_HelpCreate(state)); + controlState_RegisterCommand(state, controlAddRoute_HelpCreate(state)); + controlState_RegisterCommand(state, controlAddConnection_Create(state)); + controlState_RegisterCommand(state, controlAddRoute_Create(state)); + controlState_RegisterCommand(state, controlAddPunting_Create(state)); + controlState_RegisterCommand(state, controlAddPunting_HelpCreate(state)); +} + +static CommandReturn _controlAdd_Execute(CommandParser *parser, CommandOps *ops, + PARCList *args) { + return _controlAdd_HelpExecute(parser, ops, args); +} diff --git a/hicn-light/src/config/controlAdd.h b/hicn-light/src/config/controlAdd.h new file mode 100755 index 000000000..e1955f200 --- /dev/null +++ b/hicn-light/src/config/controlAdd.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_Add.h + * @brief Command-line "add" node + * + * Implements the "add" node of the CLI tree + * + * + */ + +#ifndef control_Add_h +#define control_Add_h + +#include + +CommandOps *webControlAdd_Create(ControlState *state); +CommandOps *controlAdd_CreateHelp(ControlState *state); +#endif // control_Add_h diff --git a/hicn-light/src/config/controlAddConnection.c b/hicn-light/src/config/controlAddConnection.c new file mode 100755 index 000000000..a0a966ddf --- /dev/null +++ b/hicn-light/src/config/controlAddConnection.c @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +// =================================================== + +static void _controlAddConnection_Init(CommandParser *parser, CommandOps *ops); +static CommandReturn _controlAddConnection_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlAddConnection_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +// =================================================== + +static CommandReturn _controlAddConnection_HIcnHelpExecute( + CommandParser *parser, CommandOps *ops, PARCList *args); +static CommandReturn _controlAddConnection_HIcnExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static CommandReturn _controlAddConnection_UdpHelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlAddConnection_UdpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static CommandReturn _controlAddConnection_TcpHelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlAddConnection_TcpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +// =================================================== + +static const char *_commandAddConnection = "add connection"; +static const char *_commandAddConnectionHIcn = "add connection hicn"; +static const char *_commandAddConnectionUdp = "add connection udp"; +static const char *_commandAddConnectionTcp = "add connection tcp"; +static const char *_commandAddConnectionHelp = "help add connection"; +static const char *_commandAddConnectionHIcnHelp = "help add connection hicn"; +static const char *_commandAddConnectionUdpHelp = "help add connection udp"; +static const char *_commandAddConnectionTcpHelp = "help add connection tcp"; + +// =================================================== + +CommandOps *controlAddConnection_Create(ControlState *state) { + return commandOps_Create(state, _commandAddConnection, + _controlAddConnection_Init, + _controlAddConnection_Execute, commandOps_Destroy); +} + +CommandOps *controlAddConnection_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandAddConnectionHelp, NULL, + _controlAddConnection_HelpExecute, + commandOps_Destroy); +} + +// =================================================== + +static CommandOps *_controlAddConnection_HIcnCreate(ControlState *state) { + return commandOps_Create(state, _commandAddConnectionHIcn, NULL, + _controlAddConnection_HIcnExecute, + commandOps_Destroy); +} + +static CommandOps *_controlAddConnection_UdpCreate(ControlState *state) { + return commandOps_Create(state, _commandAddConnectionUdp, NULL, + _controlAddConnection_UdpExecute, + commandOps_Destroy); +} + +static CommandOps *_controlAddConnection_TcpCreate(ControlState *state) { + return commandOps_Create(state, _commandAddConnectionTcp, NULL, + _controlAddConnection_TcpExecute, + commandOps_Destroy); +} + +// =================================================== + +static CommandOps *_controlAddConnection_HIcnHelpCreate(ControlState *state) { + return commandOps_Create(state, _commandAddConnectionHIcnHelp, NULL, + _controlAddConnection_HIcnHelpExecute, + commandOps_Destroy); +} + +static CommandOps *_controlAddConnection_UdpHelpCreate(ControlState *state) { + return commandOps_Create(state, _commandAddConnectionUdpHelp, NULL, + _controlAddConnection_UdpHelpExecute, + commandOps_Destroy); +} + +static CommandOps *_controlAddConnection_TcpHelpCreate(ControlState *state) { + return commandOps_Create(state, _commandAddConnectionTcpHelp, NULL, + _controlAddConnection_TcpHelpExecute, + commandOps_Destroy); +} + +// =================================================== + +static CommandReturn _controlAddConnection_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("Available commands:\n"); + printf(" %s\n", _commandAddConnectionHIcn); + printf(" %s\n", _commandAddConnectionUdp); + printf(" %s\n", _commandAddConnectionTcp); + printf("\n"); + return CommandReturn_Success; +} + +static void _controlAddConnection_Init(CommandParser *parser, CommandOps *ops) { + ControlState *state = ops->closure; + controlState_RegisterCommand(state, + _controlAddConnection_HIcnHelpCreate(state)); + controlState_RegisterCommand(state, + _controlAddConnection_UdpHelpCreate(state)); + controlState_RegisterCommand(state, + _controlAddConnection_TcpHelpCreate(state)); + + controlState_RegisterCommand(state, _controlAddConnection_HIcnCreate(state)); + controlState_RegisterCommand(state, _controlAddConnection_UdpCreate(state)); + controlState_RegisterCommand(state, _controlAddConnection_TcpCreate(state)); +} + +static CommandReturn _controlAddConnection_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + return _controlAddConnection_HelpExecute(parser, ops, args); +} + +// =================================================== +// functions general to all connection types + +/** + * Create a tunnel in the forwarder based on the addresses + * + * Caller retains ownership of memory. + * The symbolic name will be used to refer to this connection. It must be unqiue + * otherwise the forwarder will reject this commend. + * + * @param [in] parser An allocated CommandParser + * @param [in] ops Allocated CommandOps (needed to extract ControlState) + * @param [in] localAddress the local IP and port. The port may be the wildcard + * value. + * @param [in] remoteAddress The remote IP and port (both must be specified) + * @param [in] tunnelType The tunneling protocol + * @param [in] symbolic The symbolic name for the connection (must be unique) + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * struct sockaddr_in *anyAddress = parcNetwork_SockInet4AddressAny(); + * struct sockaddr_in *remote = + * parcNetwork_SockInet4Address("192.168.1.2", 9695); + * + * Address *localAddress = addressCreateFromInet(anyAddress); + * Address *remoteAddress = addressCreateFromInet(remote); + * + * control_CreateTunnel(state, localAddress, remoteAddress, IPTUN_TCP, + * "conn7"); + * + * addressDestroy(&localAddress); + * addressDestroy(&remoteAddress); + * parcMemory_Deallocate((void **)&remote); + * parcMemory_Deallocate((void **)&anyAddress); + * } + * @endcode + */ + +static CommandReturn _controlAddConnection_CreateTunnel( + CommandParser *parser, CommandOps *ops, const char *local_ip, + const char *local_port, const char *remote_ip, const char *remote_port, + connection_type tunnelType, const char *symbolic) { + ControlState *state = ops->closure; + // a request like this always has an interface index of 0 [FIELD REMOVED] + // unsigned int interfaceIndex = 0; + + // allocate command payload + add_connection_command *addConnectionCommand = + parcMemory_AllocateAndClear(sizeof(add_connection_command)); + + // check and set IP addresses + if (inet_pton(AF_INET, remote_ip, &addConnectionCommand->remoteIp.ipv4) == + 1 && + inet_pton(AF_INET, local_ip, &addConnectionCommand->localIp.ipv4) == 1) { + addConnectionCommand->ipType = ADDR_INET; + + } else if (inet_pton(AF_INET6, remote_ip, + &addConnectionCommand->remoteIp.ipv6) == 1 && + inet_pton(AF_INET6, local_ip, + &addConnectionCommand->localIp.ipv6) == 1) { + addConnectionCommand->ipType = ADDR_INET6; + + } else { + printf("Error: local address %s not same type as remote address %s\n", + local_ip, remote_ip); + parcMemory_Deallocate(&addConnectionCommand); + return CommandReturn_Failure; + } + + // Fill remaining payload fields + addConnectionCommand->connectionType = tunnelType; + strcpy(addConnectionCommand->symbolic, symbolic); + addConnectionCommand->remotePort = htons((uint16_t)atoi(remote_port)); + addConnectionCommand->localPort = htons((uint16_t)atoi(local_port)); + + // send message and receive response + struct iovec *response = + utils_SendRequest(state, ADD_CONNECTION, addConnectionCommand, + sizeof(add_connection_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} + +static CommandReturn _controlAddConnection_IpHelp(CommandParser *parser, + CommandOps *ops, + PARCList *args, + const char *protocol) { + printf("add connection hicn \n"); + printf( + "add connection udp \n"); + printf( + " : symbolic name, e.g. 'conn1' (must be " + "unique, start with alpha)\n"); + printf( + " : the IPv4 or IPv6 or hostname of the remote system\n"); + printf(" : optional local IP address to bind to\n"); + printf("\n"); + return CommandReturn_Success; +} + +static CommandReturn _controlAddConnection_HIcnHelpExecute( + CommandParser *parser, CommandOps *ops, PARCList *args) { + _controlAddConnection_IpHelp(parser, ops, args, "hicn"); + + return CommandReturn_Success; +} + +static CommandReturn _controlAddConnection_HIcnExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + static const int _indexSymbolic = 3; + static const int _indexRemAddr = 4; + static const int _indexLocAddr = 5; + + if (parcList_Size(args) != 6) { + _controlAddConnection_HIcnHelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + char *symbolic = parcList_GetAtIndex(args, _indexSymbolic); + + if (!utils_ValidateSymbolicName(symbolic)) { + printf( + "Invalid symbolic name. Must begin with alpha and contain only " + "alphanum.\n"); + return CommandReturn_Failure; + } + + char *remote_ip = parcList_GetAtIndex(args, _indexRemAddr); + char *local_ip = parcList_GetAtIndex(args, _indexLocAddr); + char *port = "1234"; // this is a random port number that will be ignored + + return _controlAddConnection_CreateTunnel( + parser, ops, local_ip, port, remote_ip, port, HICN_CONN, symbolic); +} + +static CommandReturn _controlAddConnection_UdpHelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + _controlAddConnection_IpHelp(parser, ops, args, "udp"); + + return CommandReturn_Success; +} + +static CommandReturn _controlAddConnection_UdpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + static const int _indexSymbolic = 3; + static const int _indexRemAddr = 4; + static const int _indexRemPort = 5; + static const int _indexLocAddr = 6; + static const int _indexLocPort = 7; + + if (parcList_Size(args) != 8) { + _controlAddConnection_UdpHelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + char *symbolic = parcList_GetAtIndex(args, _indexSymbolic); + + if (!utils_ValidateSymbolicName(symbolic)) { + printf( + "Invalid symbolic name. Must begin with alpha and contain only " + "alphanum.\n"); + return CommandReturn_Failure; + } + + char *remote_ip = parcList_GetAtIndex(args, _indexRemAddr); + char *local_ip = parcList_GetAtIndex(args, _indexLocAddr); + + char *remote_port = parcList_GetAtIndex(args, _indexRemPort); + char *local_port = parcList_GetAtIndex(args, _indexLocPort); + + return _controlAddConnection_CreateTunnel(parser, ops, local_ip, local_port, + remote_ip, remote_port, UDP_CONN, + symbolic); +} + +static CommandReturn _controlAddConnection_TcpHelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + _controlAddConnection_IpHelp(parser, ops, args, "tcp"); + + return CommandReturn_Success; +} + +static CommandReturn _controlAddConnection_TcpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + static const int _indexSymbolic = 3; + static const int _indexRemAddr = 4; + static const int _indexRemPort = 5; + static const int _indexLocAddr = 6; + static const int _indexLocPort = 7; + + if (parcList_Size(args) != 8) { + _controlAddConnection_UdpHelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + char *symbolic = parcList_GetAtIndex(args, _indexSymbolic); + + if (!utils_ValidateSymbolicName(symbolic)) { + printf( + "Invalid symbolic name. Must begin with alpha and contain only " + "alphanum.\n"); + return CommandReturn_Failure; + } + + char *remote_ip = parcList_GetAtIndex(args, _indexRemAddr); + char *local_ip = parcList_GetAtIndex(args, _indexLocAddr); + + char *remote_port = parcList_GetAtIndex(args, _indexRemPort); + char *local_port = parcList_GetAtIndex(args, _indexLocPort); + + return _controlAddConnection_CreateTunnel(parser, ops, local_ip, local_port, + remote_ip, remote_port, TCP_CONN, + symbolic); +} diff --git a/hicn-light/src/config/controlAddConnection.h b/hicn-light/src/config/controlAddConnection.h new file mode 100755 index 000000000..788306989 --- /dev/null +++ b/hicn-light/src/config/controlAddConnection.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_AddConnection.h + * @brief Command-line "add connection" node + * + * Implements the "add connection" node of the CLI tree + * + * + */ + +#ifndef controlAddConnection_h +#define controlAddConnection_h + +#include +CommandOps *controlAddConnection_Create(ControlState *state); +CommandOps *controlAddConnection_HelpCreate(ControlState *state); +#endif // controlAddConnection_h diff --git a/hicn-light/src/config/controlAddListener.c b/hicn-light/src/config/controlAddListener.c new file mode 100755 index 000000000..8f687c3a6 --- /dev/null +++ b/hicn-light/src/config/controlAddListener.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include +#include + +static CommandReturn _controlAddListener_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlAddListener_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *command_add_listener = "add listener"; +static const char *command_help_add_listener = "help add listener"; + +CommandOps *controlAddListener_Create(ControlState *state) { + return commandOps_Create(state, command_add_listener, NULL, + _controlAddListener_Execute, commandOps_Destroy); +} + +CommandOps *controlAddListener_HelpCreate(ControlState *state) { + return commandOps_Create(state, command_help_add_listener, NULL, + _controlAddListener_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static const int _indexProtocol = 2; +static const int _indexSymbolic = 3; +static const int _indexAddress = 4; +static const int _indexPort = 5; + +static CommandReturn _controlAddListener_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("commands:\n"); + printf(" add listener hicn \n"); + printf(" add listener udp \n"); + printf(" add listener tcp \n"); + printf("\n"); + printf( + " symbolic: User defined name for listener, must start with " + "alpha and be alphanum\n"); + printf(" protocol: hicn | udp\n"); + printf( + " localAddress: IPv4 or IPv6 address (or prefix protocol = hicn) " + "assigend to the local interface\n"); + printf(" port: Udp port\n"); + printf("\n"); + printf("Notes:\n"); + printf(" The symblic name must be unique or the source will reject it.\n"); + printf( + " If protocol = hinc: the address 0::0 indicates the main listern, " + "for which we can set punting rules.\n"); + return CommandReturn_Success; +} + +static CommandReturn _CreateListener(CommandParser *parser, CommandOps *ops, + const char *symbolic, const char *addr, + const char *port, listener_mode mode, + connection_type type) { + ControlState *state = ops->closure; + + // allocate command payload + add_listener_command *addListenerCommand = + parcMemory_AllocateAndClear(sizeof(add_listener_command)); + + // check and set IP address + if (inet_pton(AF_INET, addr, &addListenerCommand->address.ipv4) == 1) { + addListenerCommand->addressType = ADDR_INET; + + } else if (inet_pton(AF_INET6, addr, &addListenerCommand->address.ipv6) == + 1) { + addListenerCommand->addressType = ADDR_INET6; + + } else { + printf("Error: %s is not a valid network address \n", addr); + parcMemory_Deallocate(&addListenerCommand); + return CommandReturn_Failure; + } + + // Fill remaining payload fields + addListenerCommand->listenerMode = mode; + addListenerCommand->connectionType = type; + addListenerCommand->port = htons((uint16_t)atoi(port)); + strcpy(addListenerCommand->symbolic, symbolic); + + // send message and receive response + struct iovec *response = utils_SendRequest( + state, ADD_LISTENER, addListenerCommand, sizeof(add_listener_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} + +static CommandReturn _controlAddListener_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 5 && parcList_Size(args) != 6) { + _controlAddListener_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + CommandReturn result = CommandReturn_Failure; + + const char *symbolic = parcList_GetAtIndex(args, _indexSymbolic); + + if (!utils_ValidateSymbolicName(symbolic)) { + printf( + "Error: symbolic name must begin with an alpha and be alphanum " + "after\n"); + return result; + } + + const char *host = parcList_GetAtIndex(args, _indexAddress); + const char *protocol = parcList_GetAtIndex(args, _indexProtocol); + + if ((strcasecmp("hicn", protocol) == 0)) { + const char *port = + "1234"; // this is a random port number that will be ignored + + // here we discard the prefix len if it exists, since we don't use it in + // code but we let libhicn to find the right ip address. + return _CreateListener(parser, ops, symbolic, host, port, HICN_MODE, + HICN_CONN); + } + + const char *port = parcList_GetAtIndex(args, _indexPort); + + if ((strcasecmp("udp", protocol) == 0)) { + return _CreateListener(parser, ops, symbolic, host, port, IP_MODE, + UDP_CONN); + } else if ((strcasecmp("tcp", protocol) == 0)) { + return _CreateListener(parser, ops, symbolic, host, port, IP_MODE, + TCP_CONN); + } else { + _controlAddListener_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + if (result == CommandReturn_Failure) printf("creation failed\n"); + + return result; +} diff --git a/hicn-light/src/config/controlAddListener.h b/hicn-light/src/config/controlAddListener.h new file mode 100755 index 000000000..d3fc55aaf --- /dev/null +++ b/hicn-light/src/config/controlAddListener.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_AddListener.h + * @brief Add a listener to an interface + * + * <#Detailed Description#> + * + */ + +#ifndef Control_AddListener_h +#define Control_AddListener_h + +#include +CommandOps *controlAddListener_Create(ControlState *state); +CommandOps *controlAddListener_HelpCreate(ControlState *state); +#endif // Control_AddListener_h diff --git a/hicn-light/src/config/controlAddPunting.c b/hicn-light/src/config/controlAddPunting.c new file mode 100755 index 000000000..bd87e517c --- /dev/null +++ b/hicn-light/src/config/controlAddPunting.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include +#include + +static CommandReturn _controlAddPunting_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlAddPunting_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandAddPunting = "add punting"; +static const char *_commandAddPuntingHelp = "help add punting"; + +static const int _indexSymbolic = 2; +static const int _indexPrefix = 3; + +CommandOps *controlAddPunting_Create(ControlState *state) { + return commandOps_Create(state, _commandAddPunting, NULL, + _controlAddPunting_Execute, commandOps_Destroy); +} + +CommandOps *controlAddPunting_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandAddPuntingHelp, NULL, + _controlAddPunting_HelpExecute, commandOps_Destroy); +} + +// ===================================================== + +static CommandReturn _controlAddPunting_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("add punting \n"); + printf(" : listener symbolic name\n"); + printf( + "
: prefix to add as a punting rule. (example " + "1234::0/64)\n"); + printf("\n"); + + return CommandReturn_Success; +} + +static CommandReturn _controlAddPunting_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + ControlState *state = ops->closure; + + if (parcList_Size(args) != 4) { + _controlAddPunting_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + const char *symbolicOrConnid = parcList_GetAtIndex(args, _indexSymbolic); + + if (!utils_ValidateSymbolicName(symbolicOrConnid) && + !utils_IsNumber(symbolicOrConnid)) { + printf( + "ERROR: Invalid symbolic or connid:\n" + "symbolic name must begin with an alpha followed by alphanum;\nconnid " + "must be an integer\n"); + return CommandReturn_Failure; + } + + const char *prefixStr = parcList_GetAtIndex(args, _indexPrefix); + char addr[strlen(prefixStr) + 1]; + + // separate address and len + char *slash; + uint32_t len = 0; + strcpy(addr, prefixStr); + slash = strrchr(addr, '/'); + if (slash != NULL) { + len = atoi(slash + 1); + *slash = '\0'; + } + + if (len == 0) { + printf("ERROR: a prefix can not be of length 0\n"); + return CommandReturn_Failure; + } + + // allocate command payload + add_punting_command *addPuntingCommand = + parcMemory_AllocateAndClear(sizeof(add_punting_command)); + + // check and set IP address + if (inet_pton(AF_INET, addr, &addPuntingCommand->address.ipv4) == 1) { + if (len > 32) { + printf("ERROR: exceeded INET mask length, max=32\n"); + parcMemory_Deallocate(&addPuntingCommand); + return CommandReturn_Failure; + } + addPuntingCommand->addressType = ADDR_INET; + } else if (inet_pton(AF_INET6, addr, &addPuntingCommand->address.ipv6) == 1) { + if (len > 128) { + printf("ERROR: exceeded INET6 mask length, max=128\n"); + parcMemory_Deallocate(&addPuntingCommand); + return CommandReturn_Failure; + } + addPuntingCommand->addressType = ADDR_INET6; + } else { + printf("Error: %s is not a valid network address \n", addr); + parcMemory_Deallocate(&addPuntingCommand); + return CommandReturn_Failure; + } + + // Fill remaining payload fields + addPuntingCommand->len = len; + strcpy(addPuntingCommand->symbolicOrConnid, symbolicOrConnid); + + // send message and receive response + struct iovec *response = utils_SendRequest( + state, ADD_PUNTING, addPuntingCommand, sizeof(add_punting_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} + +// ====================================================================== diff --git a/hicn-light/src/config/controlAddPunting.h b/hicn-light/src/config/controlAddPunting.h new file mode 100755 index 000000000..e4d4aac7e --- /dev/null +++ b/hicn-light/src/config/controlAddPunting.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef controlAddPunting_h +#define controlAddPunting_h + +#include +CommandOps *controlAddPunting_Create(ControlState *state); +CommandOps *controlAddPunting_HelpCreate(ControlState *state); +#endif diff --git a/hicn-light/src/config/controlAddRoute.c b/hicn-light/src/config/controlAddRoute.c new file mode 100755 index 000000000..c5ddab523 --- /dev/null +++ b/hicn-light/src/config/controlAddRoute.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include +#include + +static CommandReturn _controlAddRoute_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args); +static CommandReturn _controlAddRoute_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandAddRoute = "add route"; +static const char *_commandAddRouteHelp = "help add route"; + +CommandOps *controlAddRoute_Create(ControlState *state) { + return commandOps_Create(state, _commandAddRoute, NULL, + _controlAddRoute_Execute, commandOps_Destroy); +} + +CommandOps *controlAddRoute_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandAddRouteHelp, NULL, + _controlAddRoute_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlAddRoute_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("commands:\n"); + printf(" add route \n"); + printf("\n"); + printf(" symbolic: The symbolic name for an exgress\n"); + printf( + " connid: The egress connection id (see 'help list connections')\n"); + printf( + " prefix: The hicn name as IPv4 or IPv6 address (e.g 1234::0/64)\n"); + printf(" cost: positive integer representing cost\n"); + printf("\n"); + return CommandReturn_Success; +} + +static CommandReturn _controlAddRoute_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + ControlState *state = ops->closure; + + if (parcList_Size(args) != 5) { + _controlAddRoute_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + const char *symbolicOrConnid = parcList_GetAtIndex(args, 2); + + if (!utils_ValidateSymbolicName(symbolicOrConnid) && + !utils_IsNumber(symbolicOrConnid)) { + printf( + "ERROR: Invalid symbolic or connid:\nsymbolic name must begin with an " + "alpha followed by alphanum;\nconnid must be an integer\n"); + return CommandReturn_Failure; + } + + unsigned cost = atoi(parcList_GetAtIndex(args, 4)); + + if (cost == 0) { + printf("ERROR: cost must be positive integer, got %u from '%s'\n", cost, + (char *)parcList_GetAtIndex(args, 4)); + return CommandReturn_Failure; + } + + const char *prefixStr = parcList_GetAtIndex(args, 3); + char addr[strlen(prefixStr) + 1]; + + // separate address and len + char *slash; + uint32_t len = 0; + strcpy(addr, prefixStr); + slash = strrchr(addr, '/'); + if (slash != NULL) { + len = atoi(slash + 1); + *slash = '\0'; + } + + if (len == 0) { + printf("ERROR: a prefix can not be of length 0\n"); + return CommandReturn_Failure; + } + + // allocate command payload + add_route_command *addRouteCommand = + parcMemory_AllocateAndClear(sizeof(add_route_command)); + + // check and set IP address + if (inet_pton(AF_INET, addr, &addRouteCommand->address.ipv4) == 1) { + if (len > 32) { + printf("ERROR: exceeded INET mask length, max=32\n"); + parcMemory_Deallocate(&addRouteCommand); + return CommandReturn_Failure; + } + addRouteCommand->addressType = ADDR_INET; + } else if (inet_pton(AF_INET6, addr, &addRouteCommand->address.ipv6) == 1) { + if (len > 128) { + printf("ERROR: exceeded INET6 mask length, max=128\n"); + parcMemory_Deallocate(&addRouteCommand); + return CommandReturn_Failure; + } + addRouteCommand->addressType = ADDR_INET6; + } else { + printf("Error: %s is not a valid network address \n", addr); + parcMemory_Deallocate(&addRouteCommand); + return CommandReturn_Failure; + } + + // Fill remaining payload fields + addRouteCommand->len = len; + addRouteCommand->cost = (uint16_t)cost; + strcpy(addRouteCommand->symbolicOrConnid, symbolicOrConnid); + + // send message and receive response + struct iovec *response = utils_SendRequest(state, ADD_ROUTE, addRouteCommand, + sizeof(add_route_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlAddRoute.h b/hicn-light/src/config/controlAddRoute.h new file mode 100755 index 000000000..be0ad1368 --- /dev/null +++ b/hicn-light/src/config/controlAddRoute.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_AddRoute.h + * @brief Add a static route + * + * Implements the "add route" node of the CLI tree + * + */ + +#ifndef Control_AddRoute_h +#define Control_AddRoute_h + +#include +CommandOps *controlAddRoute_Create(ControlState *state); +CommandOps *controlAddRoute_HelpCreate(ControlState *state); +#endif // Control_AddRoute_h diff --git a/hicn-light/src/config/controlCache.c b/hicn-light/src/config/controlCache.c new file mode 100755 index 000000000..d7afbfe7d --- /dev/null +++ b/hicn-light/src/config/controlCache.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include +#include +#include + +static void _controlCache_Init(CommandParser *parser, CommandOps *ops); +static CommandReturn _controlCache_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args); +static CommandReturn _controlCache_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args); + +static const char *_commandCache = "cache"; +static const char *_commandCacheHelp = "help cache"; + +CommandOps *controlCache_Create(ControlState *state) { + return commandOps_Create(state, _commandCache, _controlCache_Init, + _controlCache_Execute, commandOps_Destroy); +} + +CommandOps *controlCache_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandCacheHelp, NULL, + _controlCache_HelpExecute, commandOps_Destroy); +} + +// ===================================================== + +static CommandReturn _controlCache_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + CommandOps *ops_cache_serve = controlCacheServe_HelpCreate(NULL); + CommandOps *ops_cache_store = controlCacheStore_HelpCreate(NULL); + CommandOps *ops_cache_clear = controlCacheClear_HelpCreate(NULL); + + printf("Available commands:\n"); + printf(" %s\n", ops_cache_serve->command); + printf(" %s\n", ops_cache_store->command); + printf(" %s\n", ops_cache_clear->command); + printf("\n"); + + commandOps_Destroy(&ops_cache_serve); + commandOps_Destroy(&ops_cache_store); + commandOps_Destroy(&ops_cache_clear); + + return CommandReturn_Success; +} + +static void _controlCache_Init(CommandParser *parser, CommandOps *ops) { + ControlState *state = ops->closure; + controlState_RegisterCommand(state, controlCacheServe_HelpCreate(state)); + controlState_RegisterCommand(state, controlCacheStore_HelpCreate(state)); + controlState_RegisterCommand(state, controlCacheClear_HelpCreate(state)); + controlState_RegisterCommand(state, controlCacheServe_Create(state)); + controlState_RegisterCommand(state, controlCacheStore_Create(state)); + controlState_RegisterCommand(state, controlCacheClear_Create(state)); +} + +static CommandReturn _controlCache_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + return _controlCache_HelpExecute(parser, ops, args); +} + +// ====================================================================== diff --git a/hicn-light/src/config/controlCache.h b/hicn-light/src/config/controlCache.h new file mode 100755 index 000000000..a3614fce1 --- /dev/null +++ b/hicn-light/src/config/controlCache.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef controlCache_h +#define controlCache_h + +#include +CommandOps *controlCache_Create(ControlState *state); +CommandOps *controlCache_HelpCreate(ControlState *state); +#endif // controlCache_h diff --git a/hicn-light/src/config/controlCacheClear.c b/hicn-light/src/config/controlCacheClear.c new file mode 100755 index 000000000..c5a4e9fde --- /dev/null +++ b/hicn-light/src/config/controlCacheClear.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include + +static CommandReturn _controlCacheClear_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlCacheClear_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandCacheClear = "cache clear"; +static const char *_commandCacheClearHelp = "help cache clear"; + +// ==================================================== + +CommandOps *controlCacheClear_Create(ControlState *state) { + return commandOps_Create(state, _commandCacheClear, NULL, + _controlCacheClear_Execute, commandOps_Destroy); +} + +CommandOps *controlCacheClear_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandCacheClearHelp, NULL, + _controlCacheClear_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlCacheClear_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("cache clear\n"); + printf("\n"); + + return CommandReturn_Success; +} + +static CommandReturn _controlCacheClear_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 2) { + _controlCacheClear_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + ControlState *state = ops->closure; + // send message and receive response + struct iovec *response = utils_SendRequest(state, CACHE_CLEAR, NULL, 0); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlCacheClear.h b/hicn-light/src/config/controlCacheClear.h new file mode 100755 index 000000000..348ddba12 --- /dev/null +++ b/hicn-light/src/config/controlCacheClear.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file controlCacheClear.h + * @brief Clear the cache + * + * Removes all the cached data form the local content store (if available) + * + */ + +#ifndef Control_CacheClear_h +#define Control_CacheClear_h + +#include +CommandOps *controlCacheClear_Create(ControlState *state); +CommandOps *controlCacheClear_HelpCreate(ControlState *state); +#endif // Control_CacheClear_h diff --git a/hicn-light/src/config/controlCacheServe.c b/hicn-light/src/config/controlCacheServe.c new file mode 100755 index 000000000..85d598025 --- /dev/null +++ b/hicn-light/src/config/controlCacheServe.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include + +static CommandReturn _controlCacheServe_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlCacheServe_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandCacheServe = "cache serve"; +static const char *_commandCacheServeHelp = "help cache serve"; + +// ==================================================== + +CommandOps *controlCacheServe_Create(ControlState *state) { + return commandOps_Create(state, _commandCacheServe, NULL, + _controlCacheServe_Execute, commandOps_Destroy); +} + +CommandOps *controlCacheServe_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandCacheServeHelp, NULL, + _controlCacheServe_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlCacheServe_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("cache serve [on|off]\n"); + printf("\n"); + + return CommandReturn_Success; +} + +static CommandReturn _controlCacheServe_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 3) { + _controlCacheServe_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + bool active; + if (strcmp(parcList_GetAtIndex(args, 2), "on") == 0) { + active = true; + } else if (strcmp(parcList_GetAtIndex(args, 2), "off") == 0) { + active = false; + } else { + _controlCacheServe_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + cache_serve_command *cacheServeCommand = + parcMemory_AllocateAndClear(sizeof(cache_serve_command)); + if (active) { + cacheServeCommand->activate = ACTIVATE_ON; + } else { + cacheServeCommand->activate = ACTIVATE_OFF; + } + + ControlState *state = ops->closure; + // send message and receive response + struct iovec *response = utils_SendRequest( + state, CACHE_SERVE, cacheServeCommand, sizeof(cache_serve_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlCacheServe.h b/hicn-light/src/config/controlCacheServe.h new file mode 100755 index 000000000..4bcec51f0 --- /dev/null +++ b/hicn-light/src/config/controlCacheServe.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef Control_CacheServe_h +#define Control_CacheServe_h + +#include +CommandOps *controlCacheServe_Create(ControlState *state); +CommandOps *controlCacheServe_HelpCreate(ControlState *state); +#endif // Control_CacheServe_h diff --git a/hicn-light/src/config/controlCacheStore.c b/hicn-light/src/config/controlCacheStore.c new file mode 100755 index 000000000..3bbb34386 --- /dev/null +++ b/hicn-light/src/config/controlCacheStore.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include + +static CommandReturn _controlCacheStore_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlCacheStore_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandCacheStore = "cache store"; +static const char *_commandCacheStoreHelp = "help cache store"; + +// ==================================================== + +CommandOps *controlCacheStore_Create(ControlState *state) { + return commandOps_Create(state, _commandCacheStore, NULL, + _controlCacheStore_Execute, commandOps_Destroy); +} + +CommandOps *controlCacheStore_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandCacheStoreHelp, NULL, + _controlCacheStore_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlCacheStore_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("cache store [on|off]\n"); + printf("\n"); + + return CommandReturn_Success; +} + +static CommandReturn _controlCacheStore_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 3) { + _controlCacheStore_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + bool active; + if (strcmp(parcList_GetAtIndex(args, 2), "on") == 0) { + active = true; + } else if (strcmp(parcList_GetAtIndex(args, 2), "off") == 0) { + active = false; + } else { + _controlCacheStore_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + cache_store_command *cacheStoreCommand = + parcMemory_AllocateAndClear(sizeof(cache_store_command)); + if (active) { + cacheStoreCommand->activate = ACTIVATE_ON; + } else { + cacheStoreCommand->activate = ACTIVATE_OFF; + } + + ControlState *state = ops->closure; + // send message and receive response + struct iovec *response = utils_SendRequest( + state, CACHE_STORE, cacheStoreCommand, sizeof(cache_store_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlCacheStore.h b/hicn-light/src/config/controlCacheStore.h new file mode 100755 index 000000000..ef5aca504 --- /dev/null +++ b/hicn-light/src/config/controlCacheStore.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef Control_CacheStore_h +#define Control_CacheStore_h + +#include +CommandOps *controlCacheStore_Create(ControlState *state); +CommandOps *controlCacheStore_HelpCreate(ControlState *state); +#endif // Control_CacheStore_h diff --git a/hicn-light/src/config/controlList.c b/hicn-light/src/config/controlList.c new file mode 100755 index 000000000..8afaa60dc --- /dev/null +++ b/hicn-light/src/config/controlList.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include +//#include +#include +#include + +static void _controlList_Init(CommandParser *parser, CommandOps *ops); +static CommandReturn _controlList_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args); +static CommandReturn _controlList_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args); + +static const char *_commandList = "list"; +static const char *_commandListHelp = "help list"; + +CommandOps *controlList_Create(ControlState *state) { + return commandOps_Create(state, _commandList, _controlList_Init, + _controlList_Execute, commandOps_Destroy); +} + +CommandOps *controlList_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandListHelp, NULL, + _controlList_HelpExecute, commandOps_Destroy); +} + +// ===================================================== + +static CommandReturn _controlList_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + CommandOps *ops_list_connections = controlListConnections_HelpCreate(NULL); + // CommandOps *ops_list_interfaces = controlListInterfaces_HelpCreate(NULL); + CommandOps *ops_list_routes = controlListRoutes_HelpCreate(NULL); + CommandOps *ops_list_listeners = controlListListeners_HelpCreate(NULL); + + printf("Available commands:\n"); + printf(" %s\n", ops_list_connections->command); + // printf(" %s\n", ops_list_interfaces->command); + printf(" %s\n", ops_list_routes->command); + printf(" %s\n", ops_list_listeners->command); + printf("\n"); + + commandOps_Destroy(&ops_list_connections); + // commandOps_Destroy(&ops_list_interfaces); + commandOps_Destroy(&ops_list_routes); + commandOps_Destroy(&ops_list_listeners); + + return CommandReturn_Success; +} + +static void _controlList_Init(CommandParser *parser, CommandOps *ops) { + ControlState *state = ops->closure; + controlState_RegisterCommand(state, controlListConnections_HelpCreate(state)); + // controlState_RegisterCommand(state, + // controlListInterfaces_HelpCreate(state)); + controlState_RegisterCommand(state, controlListListeners_HelpCreate(state)); + controlState_RegisterCommand(state, controlListRoutes_HelpCreate(state)); + controlState_RegisterCommand(state, controlListConnections_Create(state)); + // controlState_RegisterCommand(state, controlListInterfaces_Create(state)); + controlState_RegisterCommand(state, controlListRoutes_Create(state)); + controlState_RegisterCommand(state, controlListListeners_Create(state)); +} + +static CommandReturn _controlList_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + return _controlList_HelpExecute(parser, ops, args); +} + +// ====================================================================== diff --git a/hicn-light/src/config/controlList.h b/hicn-light/src/config/controlList.h new file mode 100755 index 000000000..53197331f --- /dev/null +++ b/hicn-light/src/config/controlList.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_List.h + * @brief Root node for the "list" commands + * + * Implements the "list" node of the CLI tree. + * + */ + +#ifndef controlList_h +#define controlList_h + +#include +CommandOps *controlList_Create(ControlState *state); +CommandOps *controlList_HelpCreate(ControlState *state); +#endif // controlList_h diff --git a/hicn-light/src/config/controlListConnections.c b/hicn-light/src/config/controlListConnections.c new file mode 100755 index 000000000..474ddc45f --- /dev/null +++ b/hicn-light/src/config/controlListConnections.c @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include + +static CommandReturn _controlListConnections_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlListConnections_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandListConnections = "list connections"; +static const char *_commandListConnectionsHelp = "help list connections"; +const char *connTypeString[6] = {"GRE", "TCP", "UDP", "MCAST", "L2", "HICN"}; +const char *stateString[3] = {"UP", "DOWN", "UNKNOWN"}; + +CommandOps *controlListConnections_Create(ControlState *state) { + return commandOps_Create(state, _commandListConnections, NULL, + _controlListConnections_Execute, commandOps_Destroy); +} + +CommandOps *controlListConnections_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandListConnectionsHelp, NULL, + _controlListConnections_HelpExecute, + commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlListConnections_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("list connections: displays a 1-line summary of each connection\n"); + printf("\n"); + printf("The columns are:\n"); + printf(" connection id : an integer index for the connection\n"); + printf(" state : UP or DOWN\n"); + printf( + " local address : the local network address associated with the " + "connection\n"); + printf( + " remote address: the remote network address associated with the " + "connection\n"); + printf( + " protocol : the network protocol (tcp, udp, gre, mcast, " + "ether)\n"); + printf("\n"); + return CommandReturn_Success; +} + +static CommandReturn _controlListConnections_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 2) { + _controlListConnections_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + ControlState *state = ops->closure; + + // send message and receive response + struct iovec *response = utils_SendRequest(state, LIST_CONNECTIONS, NULL, 0); + if (!response) { // get NULL pointer = FAILURE + return CommandReturn_Failure; + } + + // Process/Print message + header_control_message *receivedHeader = + (header_control_message *)response[0].iov_base; + uint8_t *receivedPayload = (uint8_t *)response[1].iov_base; + + char *sourceString = NULL; + char *destinationString = NULL; + + // Allocate output to pass to the main function if the call is not interactive + char **commandOutputMain = NULL; + if (!controlState_IsInteractive(state) && receivedHeader->length > 0) { + commandOutputMain = + parcMemory_Allocate(sizeof(char *) * receivedHeader->length); + for (size_t j = 0; j < receivedHeader->length; j++) { + commandOutputMain[j] = parcMemory_Allocate(sizeof(char) * 128); + } + } + + // Process/Print payload + for (int i = 0; i < receivedHeader->length; i++) { + list_connections_command *listConnectionsCommand = + (list_connections_command *)(receivedPayload + + (i * sizeof(list_connections_command))); + + sourceString = utils_CommandAddressToString( + listConnectionsCommand->connectionData.ipType, + &listConnectionsCommand->connectionData.localIp, + &listConnectionsCommand->connectionData.localPort); + + destinationString = utils_CommandAddressToString( + listConnectionsCommand->connectionData.ipType, + &listConnectionsCommand->connectionData.remoteIp, + &listConnectionsCommand->connectionData.remotePort); + + PARCBufferComposer *composer = parcBufferComposer_Create(); + + parcBufferComposer_Format( + composer, "%5d %4s %s %s %s", listConnectionsCommand->connid, + stateString[listConnectionsCommand->state], sourceString, + destinationString, + connTypeString[listConnectionsCommand->connectionData.connectionType]); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + if (!controlState_IsInteractive(state)) { + strcpy(commandOutputMain[i], result); + } + + puts(result); + parcMemory_Deallocate((void **)&result); + parcBufferComposer_Release(&composer); + } + + controlState_SetCommandOutput(state, commandOutputMain); + + // DEALLOCATE + parcMemory_Deallocate((void **)&sourceString); + parcMemory_Deallocate((void **)&destinationString); + parcMemory_Deallocate(&receivedHeader); // free response[0].iov_base + parcMemory_Deallocate(&receivedPayload); // free response[1].iov_base + parcMemory_Deallocate(&response); // free iovec pointer + + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlListConnections.h b/hicn-light/src/config/controlListConnections.h new file mode 100755 index 000000000..17422c963 --- /dev/null +++ b/hicn-light/src/config/controlListConnections.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_ListConnections.h + * @brief List the current connections of hicn-light + * + * Implements the "list connections" node of the CLI tree + * + */ + +#ifndef Control_ListConnections_h +#define Control_ListConnections_h + +#include +CommandOps *controlListConnections_Create(ControlState *state); +CommandOps *controlListConnections_HelpCreate(ControlState *state); +#endif // Control_ListConnections_h diff --git a/hicn-light/src/config/controlListInterfaces.c b/hicn-light/src/config/controlListInterfaces.c new file mode 100755 index 000000000..20338b553 --- /dev/null +++ b/hicn-light/src/config/controlListInterfaces.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +static CommandReturn _controlListInterfaces_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlListInterfaces_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandListInterfaces = "list interfaces"; +static const char *_commandListInterfacesHelp = "help list interfaces"; + +// ==================================================== + +CommandOps *controlListInterfaces_Create(ControlState *state) { + return commandOps_Create(state, _commandListInterfaces, NULL, + _controlListInterfaces_Execute, commandOps_Destroy); +} + +CommandOps *controlListInterfaces_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandListInterfacesHelp, NULL, + _controlListInterfaces_HelpExecute, + commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlListInterfaces_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("list interfaces\n"); + printf("\n"); + + return CommandReturn_Success; +} + +static CommandReturn _controlListInterfaces_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 2) { + _controlListInterfaces_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + //========================== NOT IMPLEMENTED + //=========================== + + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlListInterfaces.h b/hicn-light/src/config/controlListInterfaces.h new file mode 100755 index 000000000..0c0ca95cf --- /dev/null +++ b/hicn-light/src/config/controlListInterfaces.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_ListInterfaces.h + * @brief List the icn-light interfaces + * + * Implements the "list interfaces" and "help list interfaces" nodes of the + * command tree + * + */ + +#ifndef Control_ListInterfaces_h +#define Control_ListInterfaces_h + +#include +CommandOps *controlListInterfaces_Create(ControlState *state); +CommandOps *controlListInterfaces_HelpCreate(ControlState *state); +#endif // Control_ListInterfaces_h diff --git a/hicn-light/src/config/controlListListeners.c b/hicn-light/src/config/controlListListeners.c new file mode 100755 index 000000000..a149051e2 --- /dev/null +++ b/hicn-light/src/config/controlListListeners.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +static CommandReturn _controlListListeners_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlListListeners_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandListListeners = "list listeners"; +static const char *_commandListListenersHelp = "help list listeners"; +static const char *listenerType[5] = {"TCP", "UDP", "ETHER", "LOCAL", "HICN"}; + +// ==================================================== + +CommandOps *controlListListeners_Create(ControlState *state) { + return commandOps_Create(state, _commandListListeners, NULL, + _controlListListeners_Execute, commandOps_Destroy); +} + +CommandOps *controlListListeners_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandListListenersHelp, NULL, + _controlListListeners_HelpExecute, + commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlListListeners_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("list listeners\n"); + printf("\n"); + + return CommandReturn_Success; +} + +static CommandReturn _controlListListeners_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 2) { + _controlListListeners_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + ControlState *state = ops->closure; + + // send message and receive response + struct iovec *response = utils_SendRequest(state, LIST_LISTENERS, NULL, 0); + if (!response) { // get NULL pointer = FAILURE + return CommandReturn_Failure; + } + + // Process/Print message + header_control_message *receivedHeader = + (header_control_message *)response[0].iov_base; + uint8_t *receivedPayload = (uint8_t *)response[1].iov_base; + + // Allocate output to pass to the main function if the call is not interactive + char **commandOutputMain = NULL; + if (!controlState_IsInteractive(state) && receivedHeader->length > 0) { + commandOutputMain = + parcMemory_Allocate(sizeof(char *) * receivedHeader->length); + for (size_t j = 0; j < receivedHeader->length; j++) { + commandOutputMain[j] = parcMemory_Allocate(sizeof(char) * 128); + } + } + + char *addrString = NULL; + if (receivedHeader->length > 0) { + printf("%6.6s %50.70s %s\n", "iface", "address", "type"); + } else { + printf(" --- No entry in the list \n"); + } + + for (int i = 0; i < receivedHeader->length; i++) { + list_listeners_command *listListenersCommand = + (list_listeners_command *)(receivedPayload + + (i * sizeof(list_listeners_command))); + + addrString = utils_CommandAddressToString(listListenersCommand->addressType, + &listListenersCommand->address, + &listListenersCommand->port); + + PARCBufferComposer *composer = parcBufferComposer_Create(); + + parcBufferComposer_Format(composer, "%6u %50.70s %3s", + listListenersCommand->connid, addrString, + listenerType[listListenersCommand->encapType]); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + if (!controlState_IsInteractive(state)) { + strcpy(commandOutputMain[i], result); + } + + puts(result); + parcMemory_Deallocate((void **)&result); + parcBufferComposer_Release(&composer); + } + + controlState_SetCommandOutput(state, commandOutputMain); + + // DEALLOCATE + parcMemory_Deallocate((void **)&addrString); + parcMemory_Deallocate(&receivedHeader); // free response[0].iov_base + parcMemory_Deallocate(&receivedPayload); // free response[1].iov_base + parcMemory_Deallocate(&response); // free iovec pointer + + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlListListeners.h b/hicn-light/src/config/controlListListeners.h new file mode 100755 index 000000000..1f34eea56 --- /dev/null +++ b/hicn-light/src/config/controlListListeners.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_ListListeners.h + * @brief List the icn-light listeners + * + * Implements the "list listeners" and "help list listeners" nodes of the + * command tree + * + */ + +#ifndef Control_ListListeners_h +#define Control_ListListeners_h + +#include +CommandOps *controlListListeners_Create(ControlState *state); +CommandOps *controlListListeners_HelpCreate(ControlState *state); +#endif // Control_ListListeners_h diff --git a/hicn-light/src/config/controlListRoutes.c b/hicn-light/src/config/controlListRoutes.c new file mode 100755 index 000000000..4a21b5ef4 --- /dev/null +++ b/hicn-light/src/config/controlListRoutes.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include +#include + +static CommandReturn _controlListRoutes_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlListRoutes_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandListRoutes = "list routes"; +static const char *_commandListRoutesHelp = "help list routes"; + +// ==================================================== + +CommandOps *controlListRoutes_Create(ControlState *state) { + return commandOps_Create(state, _commandListRoutes, NULL, + _controlListRoutes_Execute, commandOps_Destroy); +} + +CommandOps *controlListRoutes_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandListRoutesHelp, NULL, + _controlListRoutes_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlListRoutes_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("command: list routes\n"); + printf("\n"); + printf( + "This command will fetch the prefix routing table. For each route, it " + "will list:\n"); + printf(" iface: interface\n"); + printf( + " protocol: the routing protocol, such as STATIC, CONNECTED, etc.\n"); + printf( + " type: LMP or EXACT (longest matching prefix or exact match)\n"); + printf(" cost: The route cost, lower being preferred\n"); + printf(" next: List of next hops by interface id\n"); + printf(" prefix: name prefix\n"); + printf("\n"); + return CommandReturn_Success; +} + +static CommandReturn _controlListRoutes_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 2) { + _controlListRoutes_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + ControlState *state = ops->closure; + + // send message and receive response + struct iovec *response = utils_SendRequest(state, LIST_ROUTES, NULL, 0); + if (!response) { // get NULL pointer = FAILURE + return CommandReturn_Failure; + } + + // Process/Print message + header_control_message *receivedHeader = + (header_control_message *)response[0].iov_base; + uint8_t *receivedPayload = (uint8_t *)response[1].iov_base; + + // Allocate output to pass to the main function if the call is not interactive + char **commandOutputMain = NULL; + if (!controlState_IsInteractive(state) && receivedHeader->length > 0) { + commandOutputMain = + parcMemory_Allocate(sizeof(char *) * receivedHeader->length); + for (size_t j = 0; j < receivedHeader->length; j++) { + commandOutputMain[j] = parcMemory_Allocate(sizeof(char) * 128); + } + } + + char *addrString = NULL; + in_port_t port = htons(1234); // this is a random port number that is ignored + + if (receivedHeader->length > 0) { + printf("%6.6s %8.8s %70.70s %s\n", "iface", "cost", "prefix", "len"); + } else { + printf(" --- No entry in the list \n"); + } + + for (int i = 0; i < receivedHeader->length; i++) { + list_routes_command *listRoutesCommand = + (list_routes_command *)(receivedPayload + + (i * sizeof(list_routes_command))); + + addrString = utils_CommandAddressToString( + listRoutesCommand->addressType, &listRoutesCommand->address, &port); + + PARCBufferComposer *composer = parcBufferComposer_Create(); + + parcBufferComposer_Format( + composer, "%6u %8u %70.70s %3d", listRoutesCommand->connid, + listRoutesCommand->cost, addrString, listRoutesCommand->len); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + if (!controlState_IsInteractive(state)) { + strcpy(commandOutputMain[i], result); + } + + puts(result); + parcMemory_Deallocate((void **)&result); + parcBufferComposer_Release(&composer); + } + + controlState_SetCommandOutput(state, commandOutputMain); + + // DEALLOCATE + parcMemory_Deallocate((void **)&addrString); + parcMemory_Deallocate(&receivedHeader); // free response[0].iov_base + parcMemory_Deallocate(&receivedPayload); // free response[1].iov_base + parcMemory_Deallocate(&response); // free iovec pointer + + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlListRoutes.h b/hicn-light/src/config/controlListRoutes.h new file mode 100755 index 000000000..018c88ab0 --- /dev/null +++ b/hicn-light/src/config/controlListRoutes.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_ListRoutes.h + * @brief List the icn-light routes + * + * Implements the "list routes" and "help list routes" nodes of the command tree + * + */ +#ifndef Control_ListRoutes_h +#define Control_ListRoutes_h + +#include +CommandOps *controlListRoutes_Create(ControlState *state); +CommandOps *controlListRoutes_HelpCreate(ControlState *state); +#endif // Control_ListRoutes_h diff --git a/hicn-light/src/config/controlMapMe.c b/hicn-light/src/config/controlMapMe.c new file mode 100755 index 000000000..2253f52b6 --- /dev/null +++ b/hicn-light/src/config/controlMapMe.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +static void _controlMapMe_Init(CommandParser *parser, CommandOps *ops); +static CommandReturn _controlMapMe_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args); +static CommandReturn _controlMapMe_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args); + +static const char *_commandMapMe = "mapme"; +static const char *_commandMapMeHelp = "help mapme"; + +CommandOps *controlMapMe_Create(ControlState *state) { + return commandOps_Create(state, _commandMapMe, _controlMapMe_Init, + _controlMapMe_Execute, commandOps_Destroy); +} + +CommandOps *controlMapMe_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandMapMeHelp, NULL, + _controlMapMe_HelpExecute, commandOps_Destroy); +} + +// ===================================================== + +static CommandReturn _controlMapMe_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + CommandOps *ops_mapme_enable = controlMapMeEnable_HelpCreate(NULL); + CommandOps *ops_mapme_discovery = controlMapMeDiscovery_HelpCreate(NULL); + CommandOps *ops_mapme_timescale = controlMapMeTimescale_HelpCreate(NULL); + CommandOps *ops_mapme_retx = controlMapMeRetx_HelpCreate(NULL); + + printf("Available commands:\n"); + printf(" %s\n", ops_mapme_enable->command); + printf(" %s\n", ops_mapme_discovery->command); + printf(" %s\n", ops_mapme_timescale->command); + printf(" %s\n", ops_mapme_retx->command); + printf("\n"); + + commandOps_Destroy(&ops_mapme_enable); + commandOps_Destroy(&ops_mapme_discovery); + commandOps_Destroy(&ops_mapme_timescale); + commandOps_Destroy(&ops_mapme_retx); + + return CommandReturn_Success; +} + +static void _controlMapMe_Init(CommandParser *parser, CommandOps *ops) { + ControlState *state = ops->closure; + controlState_RegisterCommand(state, controlMapMeEnable_HelpCreate(state)); + controlState_RegisterCommand(state, controlMapMeDiscovery_HelpCreate(state)); + controlState_RegisterCommand(state, controlMapMeTimescale_HelpCreate(state)); + controlState_RegisterCommand(state, controlMapMeRetx_HelpCreate(state)); + controlState_RegisterCommand(state, controlMapMeEnable_Create(state)); + controlState_RegisterCommand(state, controlMapMeDiscovery_Create(state)); + controlState_RegisterCommand(state, controlMapMeTimescale_Create(state)); + controlState_RegisterCommand(state, controlMapMeRetx_Create(state)); +} + +static CommandReturn _controlMapMe_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + return _controlMapMe_HelpExecute(parser, ops, args); +} + +// ====================================================================== diff --git a/hicn-light/src/config/controlMapMe.h b/hicn-light/src/config/controlMapMe.h new file mode 100755 index 000000000..d9cfdb82c --- /dev/null +++ b/hicn-light/src/config/controlMapMe.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef controlMapMe_h +#define controlMapMe_h + +#include +CommandOps *controlMapMe_Create(ControlState *state); +CommandOps *controlMapMe_HelpCreate(ControlState *state); +#endif // controlMapMe_h diff --git a/hicn-light/src/config/controlMapMeDiscovery.c b/hicn-light/src/config/controlMapMeDiscovery.c new file mode 100755 index 000000000..f8f4bf082 --- /dev/null +++ b/hicn-light/src/config/controlMapMeDiscovery.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +static CommandReturn _controlMapMeDiscovery_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlMapMeDiscovery_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandMapMeDiscovery = "mapme discovery"; +static const char *_commandMapMeDiscoveryHelp = "help mapme discovery"; + +// ==================================================== + +CommandOps *controlMapMeDiscovery_Create(ControlState *state) { + return commandOps_Create(state, _commandMapMeDiscovery, NULL, + _controlMapMeDiscovery_Execute, commandOps_Destroy); +} + +CommandOps *controlMapMeDiscovery_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandMapMeDiscoveryHelp, NULL, + _controlMapMeDiscovery_HelpExecute, + commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlMapMeDiscovery_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("mapme discovery [on|off]\n"); + printf("\n"); + + return CommandReturn_Success; +} + +static CommandReturn _controlMapMeDiscovery_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 3) { + _controlMapMeDiscovery_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + bool active; + if (strcmp(parcList_GetAtIndex(args, 2), "on") == 0) { + active = true; + } else if (strcmp(parcList_GetAtIndex(args, 2), "off") == 0) { + active = false; + } else { + _controlMapMeDiscovery_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + mapme_activator_command *mapmeDiscoveryCommand = + parcMemory_AllocateAndClear(sizeof(mapme_activator_command)); + if (active) { + mapmeDiscoveryCommand->activate = ACTIVATE_ON; + } else { + mapmeDiscoveryCommand->activate = ACTIVATE_OFF; + } + + ControlState *state = ops->closure; + // send message and receive response + struct iovec *response = + utils_SendRequest(state, MAPME_DISCOVERY, mapmeDiscoveryCommand, + sizeof(mapme_activator_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlMapMeDiscovery.h b/hicn-light/src/config/controlMapMeDiscovery.h new file mode 100755 index 000000000..c492fa0ab --- /dev/null +++ b/hicn-light/src/config/controlMapMeDiscovery.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef Control_MapMeDiscovery_h +#define Control_MapMeDiscovery_h + +#include +CommandOps *controlMapMeDiscovery_Create(ControlState *state); +CommandOps *controlMapMeDiscovery_HelpCreate(ControlState *state); +#endif // Control_MapMeDiscovery_h diff --git a/hicn-light/src/config/controlMapMeEnable.c b/hicn-light/src/config/controlMapMeEnable.c new file mode 100755 index 000000000..db77450e5 --- /dev/null +++ b/hicn-light/src/config/controlMapMeEnable.c @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +static CommandReturn _controlMapMeEnable_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlMapMeEnable_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandMapMeEnable = "mapme enable"; +static const char *_commandMapMeEnableHelp = "help mapme enable"; + +// ==================================================== + +CommandOps *controlMapMeEnable_Create(ControlState *state) { + return commandOps_Create(state, _commandMapMeEnable, NULL, + _controlMapMeEnable_Execute, commandOps_Destroy); +} + +CommandOps *controlMapMeEnable_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandMapMeEnableHelp, NULL, + _controlMapMeEnable_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlMapMeEnable_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("mapme enable [on|off]\n"); + printf("\n"); + + return CommandReturn_Success; +} + +static CommandReturn _controlMapMeEnable_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 3) { + _controlMapMeEnable_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + bool active; + if (strcmp(parcList_GetAtIndex(args, 2), "on") == 0) { + active = true; + } else if (strcmp(parcList_GetAtIndex(args, 2), "off") == 0) { + active = false; + } else { + _controlMapMeEnable_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + mapme_activator_command *mapmeEnableCommand = + parcMemory_AllocateAndClear(sizeof(mapme_activator_command)); + if (active) { + mapmeEnableCommand->activate = ACTIVATE_ON; + } else { + mapmeEnableCommand->activate = ACTIVATE_OFF; + } + + ControlState *state = ops->closure; + // send message and receive response + struct iovec *response = utils_SendRequest( + state, MAPME_ENABLE, mapmeEnableCommand, sizeof(mapme_activator_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlMapMeEnable.h b/hicn-light/src/config/controlMapMeEnable.h new file mode 100755 index 000000000..f7ca6204d --- /dev/null +++ b/hicn-light/src/config/controlMapMeEnable.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef Control_MapMeEnable_h +#define Control_MapMeEnable_h + +#include +CommandOps *controlMapMeEnable_Create(ControlState *state); +CommandOps *controlMapMeEnable_HelpCreate(ControlState *state); +#endif // Control_MapMeEnable_h diff --git a/hicn-light/src/config/controlMapMeRetx.c b/hicn-light/src/config/controlMapMeRetx.c new file mode 100755 index 000000000..bb16b8833 --- /dev/null +++ b/hicn-light/src/config/controlMapMeRetx.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +static CommandReturn _controlMapMeRetx_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args); +static CommandReturn _controlMapMeRetx_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandMapMeRetx = "mapme retx"; +static const char *_commandMapMeRetxHelp = "help mapme retx"; + +// ==================================================== + +CommandOps *controlMapMeRetx_Create(ControlState *state) { + return commandOps_Create(state, _commandMapMeRetx, NULL, + _controlMapMeRetx_Execute, commandOps_Destroy); +} + +CommandOps *controlMapMeRetx_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandMapMeRetxHelp, NULL, + _controlMapMeRetx_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlMapMeRetx_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("mapme retx n"); + printf("\n"); + + return CommandReturn_Success; +} + +static CommandReturn _controlMapMeRetx_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 3) { + _controlMapMeRetx_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + const char *rtx = parcList_GetAtIndex(args, 2); + if (!utils_IsNumber(rtx)) { + printf( + "ERROR: retransmission value (expressed in ms) must be a positive " + "integer \n"); + return CommandReturn_Failure; + } + + mapme_timing_command *mapmeRetxCommand = + parcMemory_AllocateAndClear(sizeof(mapme_timing_command)); + mapmeRetxCommand->timePeriod = (unsigned)strtold(rtx, NULL); + + ControlState *state = ops->closure; + // send message and receive response + struct iovec *response = utils_SendRequest( + state, MAPME_RETX, mapmeRetxCommand, sizeof(mapme_timing_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlMapMeRetx.h b/hicn-light/src/config/controlMapMeRetx.h new file mode 100755 index 000000000..611bd3663 --- /dev/null +++ b/hicn-light/src/config/controlMapMeRetx.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef Control_MapMeRetx_h +#define Control_MapMeRetx_h + +#include +CommandOps *controlMapMeRetx_Create(ControlState *state); +CommandOps *controlMapMeRetx_HelpCreate(ControlState *state); +#endif // Control_MapMeRetx_h diff --git a/hicn-light/src/config/controlMapMeTimescale.c b/hicn-light/src/config/controlMapMeTimescale.c new file mode 100755 index 000000000..9303b4b0f --- /dev/null +++ b/hicn-light/src/config/controlMapMeTimescale.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +static CommandReturn _controlMapMeTimescale_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlMapMeTimescale_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandMapMeTimescale = "mapme timescale"; +static const char *_commandMapMeTimescaleHelp = "help mapme timescale"; + +// ==================================================== + +CommandOps *controlMapMeTimescale_Create(ControlState *state) { + return commandOps_Create(state, _commandMapMeTimescale, NULL, + _controlMapMeTimescale_Execute, commandOps_Destroy); +} + +CommandOps *controlMapMeTimescale_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandMapMeTimescaleHelp, NULL, + _controlMapMeTimescale_HelpExecute, + commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlMapMeTimescale_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("mapme timescale n"); + printf("\n"); + + return CommandReturn_Success; +} + +static CommandReturn _controlMapMeTimescale_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 3) { + _controlMapMeTimescale_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + const char *ts = parcList_GetAtIndex(args, 2); + if (!utils_IsNumber(ts)) { + printf( + "ERROR: timescale value (expressed in ms) must be a positive integer " + "\n"); + return CommandReturn_Failure; + } + + mapme_timing_command *mapmeTimescaleCommand = + parcMemory_AllocateAndClear(sizeof(mapme_timing_command)); + mapmeTimescaleCommand->timePeriod = (unsigned)strtold(ts, NULL); + + ControlState *state = ops->closure; + // send message and receive response + struct iovec *response = + utils_SendRequest(state, MAPME_TIMESCALE, mapmeTimescaleCommand, + sizeof(mapme_timing_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlMapMeTimescale.h b/hicn-light/src/config/controlMapMeTimescale.h new file mode 100755 index 000000000..d4b383696 --- /dev/null +++ b/hicn-light/src/config/controlMapMeTimescale.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef Control_MapMeTimescale_h +#define Control_MapMeTimescale_h + +#include +CommandOps *controlMapMeTimescale_Create(ControlState *state); +CommandOps *controlMapMeTimescale_HelpCreate(ControlState *state); +#endif // Control_MapMeTimescale_h diff --git a/hicn-light/src/config/controlQuit.c b/hicn-light/src/config/controlQuit.c new file mode 100755 index 000000000..635fe278f --- /dev/null +++ b/hicn-light/src/config/controlQuit.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +static CommandReturn _controlQuit_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args); +static CommandReturn _controlQuit_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args); + +static const char *_commandQuit = "quit"; +static const char *_commandQuitHelp = "help quit"; + +// ==================================================== + +CommandOps *controlQuit_Create(ControlState *state) { + return commandOps_Create(state, _commandQuit, NULL, _controlQuit_Execute, + commandOps_Destroy); +} + +CommandOps *controlQuit_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandQuitHelp, NULL, + _controlQuit_HelpExecute, commandOps_Destroy); +} + +// ============================================== + +static CommandReturn _controlQuit_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + printf("Exits the interactive control program\n\n"); + return CommandReturn_Success; +} + +static CommandReturn _controlQuit_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + printf("exiting interactive shell\n"); + return CommandReturn_Exit; +} diff --git a/hicn-light/src/config/controlQuit.h b/hicn-light/src/config/controlQuit.h new file mode 100755 index 000000000..e2ba3540e --- /dev/null +++ b/hicn-light/src/config/controlQuit.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_Quit.h + * @brief The quit command + * + * Implements the "quit" and "help quit" nodes of the command tree + * + */ +#ifndef Control_Quit_h +#define Control_Quit_h + +#include +CommandOps *controlQuit_Create(ControlState *state); +CommandOps *controlQuit_HelpCreate(ControlState *state); +#endif // Control_Quit_h diff --git a/hicn-light/src/config/controlRemove.c b/hicn-light/src/config/controlRemove.c new file mode 100755 index 000000000..ede075a1b --- /dev/null +++ b/hicn-light/src/config/controlRemove.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include +#include +#include + +static void _controlRemove_Init(CommandParser *parser, CommandOps *ops); +static CommandReturn _controlRemove_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args); +static CommandReturn _controlRemove_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandRemove = "remove"; +static const char *_commandRemoveHelp = "help remove"; + +// ==================================================== + +CommandOps *controlRemove_Create(ControlState *state) { + return commandOps_Create(state, _commandRemove, _controlRemove_Init, + _controlRemove_Execute, commandOps_Destroy); +} + +CommandOps *controlRemove_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandRemoveHelp, NULL, + _controlRemove_HelpExecute, commandOps_Destroy); +} + +// ============================================== + +static CommandReturn _controlRemove_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + CommandOps *ops_remove_connection = controlRemoveConnection_Create(NULL); + CommandOps *ops_remove_route = controlRemoveRoute_Create(NULL); + CommandOps *ops_remove_punting = controlRemovePunting_Create(NULL); + + printf("Available commands:\n"); + printf(" %s\n", ops_remove_connection->command); + printf(" %s\n", ops_remove_route->command); + printf(" %s\n", ops_remove_punting->command); + printf("\n"); + + commandOps_Destroy(&ops_remove_connection); + commandOps_Destroy(&ops_remove_route); + commandOps_Destroy(&ops_remove_punting); + return CommandReturn_Success; +} + +static void _controlRemove_Init(CommandParser *parser, CommandOps *ops) { + ControlState *state = ops->closure; + controlState_RegisterCommand(state, + controlRemoveConnection_HelpCreate(state)); + controlState_RegisterCommand(state, controlRemoveRoute_HelpCreate(state)); + controlState_RegisterCommand(state, controlRemoveConnection_Create(state)); + controlState_RegisterCommand(state, controlRemoveRoute_Create(state)); + controlState_RegisterCommand(state, controlRemovePunting_Create(state)); + controlState_RegisterCommand(state, controlRemovePunting_HelpCreate(state)); +} + +static CommandReturn _controlRemove_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + return _controlRemove_HelpExecute(parser, ops, args); +} diff --git a/hicn-light/src/config/controlRemove.h b/hicn-light/src/config/controlRemove.h new file mode 100755 index 000000000..d75ecfe70 --- /dev/null +++ b/hicn-light/src/config/controlRemove.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_Remove.h + * @brief Implements the remove node of the CLI tree + * + * Implements the "remove" and "help remove" nodes of the command tree + * + */ +#ifndef controlRemove_h +#define controlRemove_h + +#include +CommandOps *controlRemove_Create(ControlState *state); +CommandOps *controlRemove_HelpCreate(ControlState *state); +#endif // controlRemove_h diff --git a/hicn-light/src/config/controlRemoveConnection.c b/hicn-light/src/config/controlRemoveConnection.c new file mode 100755 index 000000000..93365ad17 --- /dev/null +++ b/hicn-light/src/config/controlRemoveConnection.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include +#include + +static CommandReturn _controlRemoveConnection_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlRemoveConnection_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +// =================================================== + +static const char *_commandRemoveConnection = "remove connection"; +static const char *_commandRemoveConnectionHelp = "help remove connection"; + +// ==================================================== + +CommandOps *controlRemoveConnection_Create(ControlState *state) { + return commandOps_Create(state, _commandRemoveConnection, NULL, + _controlRemoveConnection_Execute, + commandOps_Destroy); +} + +CommandOps *controlRemoveConnection_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandRemoveConnectionHelp, NULL, + _controlRemoveConnection_HelpExecute, + commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlRemoveConnection_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("command:\n"); + printf(" remove connection \n"); + return CommandReturn_Success; +} + +static CommandReturn _controlRemoveConnection_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + ControlState *state = ops->closure; + + if (parcList_Size(args) != 3) { + _controlRemoveConnection_HelpExecute(parser, ops, args); + return false; + } + + if ((strcmp(parcList_GetAtIndex(args, 0), "remove") != 0) || + (strcmp(parcList_GetAtIndex(args, 1), "connection") != 0)) { + _controlRemoveConnection_HelpExecute(parser, ops, args); + return false; + } + + const char *symbolicOrConnid = parcList_GetAtIndex(args, 2); + + if (!utils_ValidateSymbolicName(symbolicOrConnid) && + !utils_IsNumber(symbolicOrConnid)) { + printf( + "ERROR: Invalid symbolic or connid:\nsymbolic name must begin with an " + "alpha followed by alphanum;\nconnid must be an integer\n"); + return CommandReturn_Failure; + } + + // allocate command payload + remove_connection_command *removeConnectionCommand = + parcMemory_AllocateAndClear(sizeof(remove_connection_command)); + // fill payload + strcpy(removeConnectionCommand->symbolicOrConnid, symbolicOrConnid); + + // send message and receive response + struct iovec *response = + utils_SendRequest(state, REMOVE_CONNECTION, removeConnectionCommand, + sizeof(remove_connection_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlRemoveConnection.h b/hicn-light/src/config/controlRemoveConnection.h new file mode 100755 index 000000000..1dd1af23b --- /dev/null +++ b/hicn-light/src/config/controlRemoveConnection.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_RemoveConnection.h + * @brief Remove a connection from the connection table + * + * Implements the "remove connection" and "help remove connection" nodes of the + * CLI tree + * + */ + +#ifndef Control_RemoveConnection_h +#define Control_RemoveConnection_h + +#include +CommandOps *controlRemoveConnection_Create(ControlState *state); +CommandOps *controlRemoveConnection_HelpCreate(ControlState *state); +#endif // Control_RemoveConnection_h diff --git a/hicn-light/src/config/controlRemovePunting.c b/hicn-light/src/config/controlRemovePunting.c new file mode 100755 index 000000000..cf4c4fbd4 --- /dev/null +++ b/hicn-light/src/config/controlRemovePunting.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +static CommandReturn _controlRemovePunting_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlRemovePunting_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +// =================================================== + +static const char *_commandRemovePunting = "remove punting"; +static const char *_commandRemovePuntingHelp = "help punting connection"; + +// ==================================================== + +CommandOps *controlRemovePunting_Create(ControlState *state) { + return commandOps_Create(state, _commandRemovePunting, NULL, + _controlRemovePunting_Execute, commandOps_Destroy); +} + +CommandOps *controlRemovePunting_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandRemovePuntingHelp, NULL, + _controlRemovePunting_HelpExecute, + commandOps_Destroy); +} + +// ==================================================== + +// ==================================================== + +static CommandReturn _controlRemovePunting_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("remove punting \n"); + return CommandReturn_Success; +} + +static CommandReturn _controlRemovePunting_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("command not implemented\n"); + return _controlRemovePunting_HelpExecute(parser, ops, args); +} + +// ================================================== diff --git a/hicn-light/src/config/controlRemovePunting.h b/hicn-light/src/config/controlRemovePunting.h new file mode 100755 index 000000000..89b1343e7 --- /dev/null +++ b/hicn-light/src/config/controlRemovePunting.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_RemovePunting.h + * + */ + +#ifndef Control_RemovePunting_h +#define Control_RemovePunting_h + +#include +CommandOps *controlRemovePunting_Create(ControlState *state); +CommandOps *controlRemovePunting_HelpCreate(ControlState *state); +#endif // Control_RemovePunting_h diff --git a/hicn-light/src/config/controlRemoveRoute.c b/hicn-light/src/config/controlRemoveRoute.c new file mode 100755 index 000000000..b9b4ed1e4 --- /dev/null +++ b/hicn-light/src/config/controlRemoveRoute.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +#include +#include + +static CommandReturn _controlRemoveRoute_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlRemoveRoute_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +// =================================================== + +static const char *_commandRemoveRoute = "remove route"; +static const char *_commandRemoveRouteHelp = "help remove route"; + +// ==================================================== + +CommandOps *controlRemoveRoute_Create(ControlState *state) { + return commandOps_Create(state, _commandRemoveRoute, NULL, + _controlRemoveRoute_Execute, commandOps_Destroy); +} + +CommandOps *controlRemoveRoute_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandRemoveRouteHelp, NULL, + _controlRemoveRoute_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlRemoveRoute_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("commands:\n"); + printf(" remove route \n"); + return CommandReturn_Success; +} + +static CommandReturn _controlRemoveRoute_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + ControlState *state = ops->closure; + + if (parcList_Size(args) != 4) { + _controlRemoveRoute_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + const char *symbolicOrConnid = parcList_GetAtIndex(args, 2); + + if (!utils_ValidateSymbolicName(symbolicOrConnid) && + !utils_IsNumber(symbolicOrConnid)) { + printf( + "ERROR: Invalid symbolic or connid:\nsymbolic name must begin with an " + "alpha followed by alphanum;\nconnid must be an integer\n"); + return CommandReturn_Failure; + } + + const char *prefixStr = parcList_GetAtIndex(args, 3); + char addr[strlen(prefixStr) + 1]; + + // separate address and len + char *slash; + uint32_t len = 0; + strcpy(addr, prefixStr); + slash = strrchr(addr, '/'); + if (slash != NULL) { + len = atoi(slash + 1); + *slash = '\0'; + } + + if (len == 0) { + printf("ERROR: a prefix can not be of length 0\n"); + return CommandReturn_Failure; + } + + // allocate command payload + remove_route_command *removeRouteCommand = + parcMemory_AllocateAndClear(sizeof(remove_route_command)); + + // check and set IP address + if (inet_pton(AF_INET, addr, &removeRouteCommand->address.ipv4) == 1) { + if (len > 32) { + printf("ERROR: exceeded INET mask length, max=32\n"); + parcMemory_Deallocate(&removeRouteCommand); + return CommandReturn_Failure; + } + removeRouteCommand->addressType = ADDR_INET; + } else if (inet_pton(AF_INET6, addr, &removeRouteCommand->address.ipv6) == + 1) { + if (len > 128) { + printf("ERROR: exceeded INET6 mask length, max=128\n"); + parcMemory_Deallocate(&removeRouteCommand); + return CommandReturn_Failure; + } + removeRouteCommand->addressType = ADDR_INET6; + } else { + printf("Error: %s is not a valid network address \n", addr); + parcMemory_Deallocate(&removeRouteCommand); + return CommandReturn_Failure; + } + + // Fill remaining payload fields + removeRouteCommand->len = len; + strcpy(removeRouteCommand->symbolicOrConnid, symbolicOrConnid); + + // send message and receive response + struct iovec *response = utils_SendRequest( + state, REMOVE_ROUTE, removeRouteCommand, sizeof(remove_route_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlRemoveRoute.h b/hicn-light/src/config/controlRemoveRoute.h new file mode 100755 index 000000000..a3c0ee46a --- /dev/null +++ b/hicn-light/src/config/controlRemoveRoute.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_RemoveRoute.h + * @brief Remove a route from the FIB + * + * Implements the "remove route" and "help remove route" nodes of the command + * tree + * + */ + +#ifndef Control_RemoveRoute_h +#define Control_RemoveRoute_h + +#include +CommandOps *controlRemoveRoute_Create(ControlState *state); +CommandOps *controlRemoveRoute_HelpCreate(ControlState *state); +#endif // Control_RemoveRoute_h diff --git a/hicn-light/src/config/controlRoot.c b/hicn-light/src/config/controlRoot.c new file mode 100755 index 000000000..5d6c5f98e --- /dev/null +++ b/hicn-light/src/config/controlRoot.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void _controlRoot_Init(CommandParser *parser, CommandOps *ops); +static CommandReturn _controlRoot_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args); +static CommandReturn _controlRoot_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args); + +static const char *_commandRoot = ""; +static const char *_commandRootHelp = "help"; + +// ==================================================== + +CommandOps *controlRoot_Create(ControlState *state) { + return commandOps_Create(state, _commandRoot, _controlRoot_Init, + _controlRoot_Execute, commandOps_Destroy); +} + +CommandOps *controlRoot_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandRootHelp, NULL, + _controlRoot_HelpExecute, commandOps_Destroy); +} + +// =================================================== + +static CommandReturn _controlRoot_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + printf("Command-line execution:\n"); + printf( + " controller [--keystore ] [--password ] " + "command\n"); + printf("\n"); + printf("Interactive execution:\n"); + printf(" controller [--keystore ] [--password ]\n"); + printf("\n"); + printf( + "If the keystore is not specified, the default path is used. Keystore " + "must exist prior to running program.\n"); + printf("If the password is not specified, the user will be prompted.\n"); + printf("\n"); + + CommandOps *ops_help_add = controlAdd_CreateHelp(NULL); + CommandOps *ops_help_list = controlList_HelpCreate(NULL); + CommandOps *ops_help_quit = controlQuit_HelpCreate(NULL); + CommandOps *ops_help_remove = controlRemove_HelpCreate(NULL); + CommandOps *ops_help_set = controlSet_HelpCreate(NULL); + CommandOps *ops_help_unset = controlUnset_HelpCreate(NULL); + CommandOps *ops_help_cache = controlCache_HelpCreate(NULL); + CommandOps *ops_help_mapme = controlMapMe_HelpCreate(NULL); + + printf("Available commands:\n"); + printf(" %s\n", ops_help_add->command); + printf(" %s\n", ops_help_list->command); + printf(" %s\n", ops_help_quit->command); + printf(" %s\n", ops_help_remove->command); + printf(" %s\n", ops_help_set->command); + printf(" %s\n", ops_help_unset->command); + printf(" %s\n", ops_help_cache->command); + printf(" %s\n", ops_help_mapme->command); + printf("\n"); + + commandOps_Destroy(&ops_help_add); + commandOps_Destroy(&ops_help_list); + commandOps_Destroy(&ops_help_quit); + commandOps_Destroy(&ops_help_remove); + commandOps_Destroy(&ops_help_set); + commandOps_Destroy(&ops_help_unset); + commandOps_Destroy(&ops_help_cache); + commandOps_Destroy(&ops_help_mapme); + + return CommandReturn_Success; +} + +static void _controlRoot_Init(CommandParser *parser, CommandOps *ops) { + ControlState *state = ops->closure; + + controlState_RegisterCommand(state, controlAdd_CreateHelp(state)); + controlState_RegisterCommand(state, controlList_HelpCreate(state)); + controlState_RegisterCommand(state, controlQuit_HelpCreate(state)); + controlState_RegisterCommand(state, controlRemove_HelpCreate(state)); + controlState_RegisterCommand(state, controlSet_HelpCreate(state)); + controlState_RegisterCommand(state, controlUnset_HelpCreate(state)); + controlState_RegisterCommand(state, controlCache_HelpCreate(state)); + controlState_RegisterCommand(state, controlMapMe_HelpCreate(state)); + + controlState_RegisterCommand(state, webControlAdd_Create(state)); + controlState_RegisterCommand(state, controlList_Create(state)); + controlState_RegisterCommand(state, controlQuit_Create(state)); + controlState_RegisterCommand(state, controlRemove_Create(state)); + controlState_RegisterCommand(state, controlSet_Create(state)); + controlState_RegisterCommand(state, controlUnset_Create(state)); + controlState_RegisterCommand(state, controlCache_Create(state)); + controlState_RegisterCommand(state, controlMapMe_Create(state)); +} + +static CommandReturn _controlRoot_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + return CommandReturn_Success; +} + +// ====================================================================== diff --git a/hicn-light/src/config/controlRoot.h b/hicn-light/src/config/controlRoot.h new file mode 100755 index 000000000..a62126eba --- /dev/null +++ b/hicn-light/src/config/controlRoot.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_Root.h + * @brief Root of the command tree + * + * Implements the root of the command tree. This is the one module that + * needs to be seeded to the control state to build the whole tree. + * + */ + +#ifndef Control_Root_h +#define Control_Root_h + +#include +CommandOps *controlRoot_Create(ControlState *state); +CommandOps *controlRoot_HelpCreate(ControlState *state); +#endif // Control_Root_h diff --git a/hicn-light/src/config/controlSet.c b/hicn-light/src/config/controlSet.c new file mode 100755 index 000000000..c6fd9aa3e --- /dev/null +++ b/hicn-light/src/config/controlSet.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +static void _controlSet_Init(CommandParser *parser, CommandOps *ops); +static CommandReturn _controlSet_Execute(CommandParser *parser, CommandOps *ops, + PARCList *args); +static CommandReturn _controlSet_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args); + +static const char *_commandSet = "set"; +static const char *_commandSetHelp = "help set"; + +// =========================================================== + +CommandOps *controlSet_Create(ControlState *state) { + return commandOps_Create(state, _commandSet, _controlSet_Init, + _controlSet_Execute, commandOps_Destroy); +} + +CommandOps *controlSet_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandSetHelp, NULL, + _controlSet_HelpExecute, commandOps_Destroy); +} + +// =========================================================== + +static void _controlSet_Init(CommandParser *parser, CommandOps *ops) { + ControlState *state = ops->closure; + controlState_RegisterCommand(state, controlSetDebug_Create(state)); + controlState_RegisterCommand(state, controlSetDebug_HelpCreate(state)); + controlState_RegisterCommand(state, controlSetStrategy_Create(state)); + controlState_RegisterCommand(state, controlSetStrategy_HelpCreate(state)); + controlState_RegisterCommand(state, controlSetWldr_Create(state)); + controlState_RegisterCommand(state, controlSetWldr_HelpCreate(state)); +} + +static CommandReturn _controlSet_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + CommandOps *ops_help_set_debug = controlSetDebug_HelpCreate(NULL); + CommandOps *ops_help_set_strategy = controlSetStrategy_HelpCreate(NULL); + CommandOps *ops_help_set_wldr = controlSetWldr_HelpCreate(NULL); + + printf("Available commands:\n"); + printf(" %s\n", ops_help_set_debug->command); + printf(" %s\n", ops_help_set_strategy->command); + printf(" %s\n", ops_help_set_wldr->command); + printf("\n"); + + commandOps_Destroy(&ops_help_set_debug); + commandOps_Destroy(&ops_help_set_strategy); + commandOps_Destroy(&ops_help_set_wldr); + return CommandReturn_Success; +} + +static CommandReturn _controlSet_Execute(CommandParser *parser, CommandOps *ops, + PARCList *args) { + return _controlSet_HelpExecute(parser, ops, args); +} diff --git a/hicn-light/src/config/controlSet.h b/hicn-light/src/config/controlSet.h new file mode 100755 index 000000000..4289aad8c --- /dev/null +++ b/hicn-light/src/config/controlSet.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_Set.h + * @brief Implements the set node of the CLI tree + * + * Implements the "set" and "help set" nodes of the command tree + * + */ +#ifndef Control_Set_h +#define Control_Set_h + +#include +CommandOps *controlSet_Create(ControlState *state); +CommandOps *controlSet_HelpCreate(ControlState *state); +#endif // Control_Set_h diff --git a/hicn-light/src/config/controlSetDebug.c b/hicn-light/src/config/controlSetDebug.c new file mode 100755 index 000000000..ca432420e --- /dev/null +++ b/hicn-light/src/config/controlSetDebug.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +static CommandReturn _controlSetDebug_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args); +static CommandReturn _controlSetDebug_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandSetDebug = "set debug"; +static const char *_commandSetDebugHelp = "help set debug"; + +// ==================================================== + +CommandOps *controlSetDebug_Create(ControlState *state) { + return commandOps_Create(state, _commandSetDebug, NULL, + _controlSetDebug_Execute, commandOps_Destroy); +} + +CommandOps *controlSetDebug_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandSetDebugHelp, NULL, + _controlSetDebug_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlSetDebug_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("set debug: will enable the debug flag for more verbose output\n"); + printf("\n"); + return CommandReturn_Success; +} + +static CommandReturn _controlSetDebug_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + if (parcList_Size(args) != 2) { + _controlSetDebug_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + ControlState *state = ops->closure; + controlState_SetDebug(state, true); + printf("Debug flag set\n\n"); + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlSetDebug.h b/hicn-light/src/config/controlSetDebug.h new file mode 100755 index 000000000..5335ebcab --- /dev/null +++ b/hicn-light/src/config/controlSetDebug.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_SetDebug.h + * @brief Sets the debug flag for more verbose output + * + * Implements the "set debug" and "help set debug" nodes of the command tree + * + */ + +#ifndef Control_SetDebug_h +#define Control_SetDebug_h + +#include +CommandOps *controlSetDebug_Create(ControlState *state); +CommandOps *controlSetDebug_HelpCreate(ControlState *state); +#endif // Control_SetDebug_h diff --git a/hicn-light/src/config/controlSetStrategy.c b/hicn-light/src/config/controlSetStrategy.c new file mode 100755 index 000000000..7b7c11762 --- /dev/null +++ b/hicn-light/src/config/controlSetStrategy.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include + +static CommandReturn _controlSetStrategy_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlSetStrategy_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandSetStrategy = "set strategy"; +static const char *_commandSetStrategyHelp = "help set strategy"; + +static const char *_commandSetStrategyOptions[LAST_STRATEGY_VALUE] = { + "loadbalancer", + "random", + "random_per_dash_segment", + "loadbalancer_with_delay", + "loadbalancer_by_rate", + "loadbalancer_best_route"}; + +// ==================================================== + +CommandOps *controlSetStrategy_Create(ControlState *state) { + return commandOps_Create(state, _commandSetStrategy, NULL, + _controlSetStrategy_Execute, commandOps_Destroy); +} + +CommandOps *controlSetStrategy_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandSetStrategyHelp, NULL, + _controlSetStrategy_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +strategy_type _validStrategy(const char *strategy) { + strategy_type validStrategy = LAST_STRATEGY_VALUE; + + for (int i = 0; i < LAST_STRATEGY_VALUE; i++) { + if (strcmp(_commandSetStrategyOptions[i], strategy) == 0) { + validStrategy = i; + break; + } + } + return validStrategy; +} + +static CommandReturn _controlSetStrategy_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("set strategy \n"); + printf("prefix: ipv4/ipv6 address (ex: 1234::/64)\n"); + printf("strategy: strategy identifier\n"); + printf("available strategies:\n"); + printf(" random\n"); + printf(" loadbalancer\n"); + printf(" random_per_dash_segment\n"); + printf(" loadbalancer_with_delay\n"); + printf("\n"); + return CommandReturn_Success; +} + +static CommandReturn _controlSetStrategy_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + ControlState *state = ops->closure; + + if (parcList_Size(args) != 4) { + _controlSetStrategy_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + if (((strcmp(parcList_GetAtIndex(args, 0), "set") != 0) || + (strcmp(parcList_GetAtIndex(args, 1), "strategy") != 0))) { + _controlSetStrategy_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + const char *prefixStr = parcList_GetAtIndex(args, 2); + char addr[strlen(prefixStr) + 1]; + // separate address and len + char *slash; + uint32_t len = UINT32_MAX; + strcpy(addr, prefixStr); + slash = strrchr(addr, '/'); + if (slash != NULL) { + len = atoi(slash + 1); + *slash = '\0'; + } + if (len == 0) { + printf("ERROR: a prefix can not be of length 0\n"); + return CommandReturn_Failure; + } + + // allocate command payload + set_strategy_command *setStrategyCommand = + parcMemory_AllocateAndClear(sizeof(set_strategy_command)); + + // check and set IP address + if (inet_pton(AF_INET, addr, &setStrategyCommand->address.ipv4) == 1) { + if (len == UINT32_MAX) { + printf("Netmask not specified: set to 32 by default\n"); + len = 32; + } else if (len > 32) { + printf("ERROR: exceeded INET mask length, max=32\n"); + parcMemory_Deallocate(&setStrategyCommand); + return CommandReturn_Failure; + } + setStrategyCommand->addressType = ADDR_INET; + } else if (inet_pton(AF_INET6, addr, &setStrategyCommand->address.ipv6) == + 1) { + if (len == UINT32_MAX) { + printf("Netmask not specified: set to 128 by default\n"); + len = 128; + } else if (len > 128) { + printf("ERROR: exceeded INET6 mask length, max=128\n"); + parcMemory_Deallocate(&setStrategyCommand); + return CommandReturn_Failure; + } + setStrategyCommand->addressType = ADDR_INET6; + } else { + printf("Error: %s is not a valid network address \n", addr); + parcMemory_Deallocate(&setStrategyCommand); + return CommandReturn_Failure; + } + + const char *strategyStr = parcList_GetAtIndex(args, 3); + // check valid strategy + strategy_type strategy; + if ((strategy = _validStrategy(strategyStr)) == LAST_STRATEGY_VALUE) { + printf("Error: invalid strategy \n"); + parcMemory_Deallocate(&setStrategyCommand); + _controlSetStrategy_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + // Fill remaining payload fields + setStrategyCommand->len = len; + setStrategyCommand->strategyType = strategy; + + // send message and receive response + struct iovec *response = utils_SendRequest( + state, SET_STRATEGY, setStrategyCommand, sizeof(set_strategy_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlSetStrategy.h b/hicn-light/src/config/controlSetStrategy.h new file mode 100755 index 000000000..53ce8912c --- /dev/null +++ b/hicn-light/src/config/controlSetStrategy.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef Control_SetStrategy_h +#define Control_SetStrategy_h + +#include +CommandOps *controlSetStrategy_Create(ControlState *state); +CommandOps *controlSetStrategy_HelpCreate(ControlState *state); +#endif // Control_SetStrategy_h diff --git a/hicn-light/src/config/controlSetWldr.c b/hicn-light/src/config/controlSetWldr.c new file mode 100755 index 000000000..9da404036 --- /dev/null +++ b/hicn-light/src/config/controlSetWldr.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include +#include + +static CommandReturn _controlSetWldr_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args); +static CommandReturn _controlSetWldr_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandSetWldr = "set wldr"; +static const char *_commandSetWldrHelp = "help set wldr"; + +// ==================================================== + +CommandOps *controlSetWldr_Create(ControlState *state) { + return commandOps_Create(state, _commandSetWldr, NULL, + _controlSetWldr_Execute, commandOps_Destroy); +} + +CommandOps *controlSetWldr_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandSetWldrHelp, NULL, + _controlSetWldr_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlSetWldr_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("set wldr \n"); + printf("\n"); + return CommandReturn_Success; +} + +static CommandReturn _controlSetWldr_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + ControlState *state = ops->closure; + + if (parcList_Size(args) != 4) { + _controlSetWldr_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + if (((strcmp(parcList_GetAtIndex(args, 0), "set") != 0) || + (strcmp(parcList_GetAtIndex(args, 1), "wldr") != 0))) { + _controlSetWldr_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + bool active; + if (strcmp(parcList_GetAtIndex(args, 2), "on") == 0) { + active = true; + } else if (strcmp(parcList_GetAtIndex(args, 2), "off") == 0) { + active = false; + } else { + _controlSetWldr_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + // check if valid connid + const char *symbolicOrConnid = parcList_GetAtIndex(args, 3); + + if (!utils_ValidateSymbolicName(symbolicOrConnid) && + !utils_IsNumber(symbolicOrConnid)) { + printf( + "ERROR: Invalid symbolic or connid:\nsymbolic name must begin with an " + "alpha followed by alphanum;\nconnid must be an integer\n"); + return CommandReturn_Failure; + } + + // allocate command payload + set_wldr_command *setWldrCommand = + parcMemory_AllocateAndClear(sizeof(set_wldr_command)); + strcpy(setWldrCommand->symbolicOrConnid, symbolicOrConnid); + if (active) { + setWldrCommand->activate = ACTIVATE_ON; + } else { + setWldrCommand->activate = ACTIVATE_OFF; + } + + // send message and receive response + struct iovec *response = utils_SendRequest(state, SET_WLDR, setWldrCommand, + sizeof(set_wldr_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlSetWldr.h b/hicn-light/src/config/controlSetWldr.h new file mode 100755 index 000000000..59c0b0fe6 --- /dev/null +++ b/hicn-light/src/config/controlSetWldr.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef Control_SetWldr_h +#define Control_SetWldr_h + +#include +CommandOps *controlSetWldr_Create(ControlState *state); +CommandOps *controlSetWldr_HelpCreate(ControlState *state); +#endif // Control_SetWldr_h diff --git a/hicn-light/src/config/controlState.c b/hicn-light/src/config/controlState.c new file mode 100755 index 000000000..d8260e8e8 --- /dev/null +++ b/hicn-light/src/config/controlState.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define SRV_IP "127.0.0.1" +#define PORT 9695 + +struct controller_state { + CommandParser *parser; + bool debugFlag; + + void *userdata; + struct iovec *(*writeRead)(ControlState *state, struct iovec *msg); + int sockfd; + char **commandOutput; + bool isInteractive; +}; + +int controlState_connectToFwdDeamon() { + int sockfd; + struct sockaddr_in servaddr; + + if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + printf("\nSocket Creation Failed \n"); + exit(EXIT_FAILURE); + } + + memset(&servaddr, 0, sizeof(servaddr)); + + // Filling server information + servaddr.sin_family = AF_INET; + servaddr.sin_port = htons(PORT); + servaddr.sin_addr.s_addr = INADDR_ANY; + + // Establish connection + if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { + printf("\nConnection Failed: hicn-light Daemon is not running \n"); + exit(EXIT_FAILURE); + } + + return sockfd; +} + +ControlState *controlState_Create( + void *userdata, + struct iovec *(*writeRead)(ControlState *state, struct iovec *msg), + bool openControllerConnetion) { + ControlState *state = parcMemory_AllocateAndClear(sizeof(ControlState)); + parcAssertNotNull(state, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ControlState)); + state->parser = commandParser_Create(); + + state->userdata = userdata; + state->writeRead = writeRead; + state->debugFlag = false; + state->commandOutput = NULL; + state->isInteractive = true; + + if (openControllerConnetion) { + state->sockfd = controlState_connectToFwdDeamon(); + } else { + state->sockfd = 2; // stderr + } + + return state; +} + +void controlState_Destroy(ControlState **statePtr) { + parcAssertNotNull(statePtr, "Parameter statePtr must be non-null"); + parcAssertNotNull(*statePtr, + "Parameter statePtr must dereference t non-null"); + ControlState *state = *statePtr; + // printf("sockid destroyed: %d\n", state->sockfd); + // close the connection with the fwd deamon + shutdown(state->sockfd, 2); + + commandParser_Destroy(&state->parser); + parcMemory_Deallocate((void **)&state); + *statePtr = NULL; +} + +void controlState_SetDebug(ControlState *state, bool debugFlag) { + parcAssertNotNull(state, "Parameter state must be non-null"); + state->debugFlag = debugFlag; + commandParser_SetDebug(state->parser, debugFlag); +} + +bool controlState_GetDebug(ControlState *state) { + parcAssertNotNull(state, "Parameter state must be non-null"); + return state->debugFlag; +} + +void controlState_RegisterCommand(ControlState *state, CommandOps *ops) { + parcAssertNotNull(state, "Parameter state must be non-null"); + commandParser_RegisterCommand(state->parser, ops); +} + +struct iovec *controlState_WriteRead(ControlState *state, struct iovec *msg) { + parcAssertNotNull(state, "Parameter state must be non-null"); + parcAssertNotNull(msg, "Parameter msg must be non-null"); + + return state->writeRead(state, msg); +} + +static PARCList *_controlState_ParseStringIntoTokens( + const char *originalString) { + PARCList *list = + parcList(parcArrayList_Create(parcArrayList_StdlibFreeFunction), + PARCArrayListAsPARCList); + + char *token; + + char *tofree = + parcMemory_StringDuplicate(originalString, strlen(originalString) + 1); + char *string = tofree; + + while ((token = strsep(&string, " \t\n")) != NULL) { + if (strlen(token) > 0) { + parcList_Add(list, strdup(token)); + } + } + + parcMemory_Deallocate((void **)&tofree); + + return list; +} + +CommandReturn controlState_DispatchCommand(ControlState *state, + PARCList *args) { + parcAssertNotNull(state, "Parameter state must be non-null"); + return commandParser_DispatchCommand(state->parser, args); +} + +int controlState_Interactive(ControlState *state) { + parcAssertNotNull(state, "Parameter state must be non-null"); + char *line = NULL; + size_t linecap = 0; + CommandReturn controlReturn = CommandReturn_Success; + + while (controlReturn != CommandReturn_Exit && !feof(stdin)) { + fputs("> ", stdout); + fflush(stdout); + ssize_t failure = getline(&line, &linecap, stdin); + parcAssertTrue(failure > -1, "Error getline"); + + PARCList *args = _controlState_ParseStringIntoTokens(line); + controlReturn = controlState_DispatchCommand(state, args); + // release and get command + parcList_Release(&args); + } + return 0; +} + +void controlState_SetCommandOutput(ControlState *state, char **commandData) { + state->commandOutput = commandData; +} + +void controlState_ReleaseCommandOutput(ControlState *state, char **commandData, + size_t commandLenght) { + for (size_t i = 0; i < commandLenght; i++) { + parcMemory_Deallocate(&commandData[i]); + } + parcMemory_Deallocate(&commandData); + state->commandOutput = NULL; +} + +char **controlState_GetCommandOutput(ControlState *state) { + return state->commandOutput; +} + +// size_t +// controlState_GetCommandLen(ControlState *state){ + +// } + +void controlState_SetInteractiveFlag(ControlState *state, bool interactive) { + state->isInteractive = interactive; +} + +bool controlState_IsInteractive(ControlState *state) { + return state->isInteractive; +} + +int controlState_GetSockfd(ControlState *state) { + parcAssertNotNull(state, "Parameter state must be non-null"); + return state->sockfd; +} + +void *controlState_GetUserdata(ControlState *state) { + parcAssertNotNull(state, "Parameter state must be non-null"); + return state->userdata; +} + +bool controlState_isConfigFile(ControlState *state) { + parcAssertNotNull(state, "Parameter state must be non-null"); + if (state->sockfd != 2) { + return false; + } else { + return true; + } +} \ No newline at end of file diff --git a/hicn-light/src/config/controlState.h b/hicn-light/src/config/controlState.h new file mode 100755 index 000000000..905c56c30 --- /dev/null +++ b/hicn-light/src/config/controlState.h @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file controlState.h + * @brief A control program for hicn-light using CLI commands + * + * Implements the state machine for the control program. It takes a "writeRead" + * function as part of the constructor. This abstracts out the backend. It + * could be a Portal from hicnLightControl program down to the forwarder or it + * could be an internal function within hicn-light. + * + */ + +#ifndef control_h +#define control_h + +#include +#include + +#include + +struct controller_state; +typedef struct controller_state ControlState; + +/** + * controlState_Create + * + * Creates the global state for the Control program. The user provides the + * writeRead function for sending and receiving the message wrapping command + * arguments. For configuration file inside hicn-light, it would make direct + * calls to Configuration -> Dispatcher. + * + * @param [in] userdata A closure passed back to the user when calling + * writeRead. + * @param [in] writeRead The function to write then read configuration messages + * to hicn-light + * + * @return non-null The control state + * + * Example: + * @code + * <#example#> + * @endcode + */ + +ControlState *controlState_Create( + void *userdata, + struct iovec *(*writeRead)(ControlState *state, struct iovec *msg), + bool openControllerConnetion); + +/** + * Destroys the control state, closing all network connections + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void controlState_Destroy(ControlState **statePtr); + +/** + * Registers a CommandOps with the system. + * + * Each command has its complete command prefix in the "command" field. + * RegisterCommand will put these command prefixes in to a tree and then match + * what a user types against the longest-matching prefix in the tree. If + * there's a match, it will call the "execute" function. + * + * @param [in] state An allocated ControlState + * @param [in] command The command to register with the system + * + * Example: + * @code + * static CommandReturn + * control_Root_Execute(CommandParser *parser, CommandOps *ops, PARCList + * *args) + * { + * printf("Root Command\n"); + * return CommandReturn_Success; + * } + * + * static CommandReturn + * control_FooBar_Execute(CommandParser *parser, CommandOps *ops, PARCList + * *args) + * { + * printf("Foo Bar Command\n"); + * return CommandReturn_Success; + * } + * + * const CommandOps control_Root = { + * .command = "", // empty string for root + * .init = NULL, + * .execute = control_Root_Execute + * }; + * + * const CommandOps control_FooBar = { + * .command = "foo bar", // empty string for root + * .init = NULL, + * .execute = control_FooBar_Execute + * }; + * + * void startup(void) + * { + * ControlState *state = controlState_Create("happy", "day"); + * controlState_RegisterCommand(state, control_FooBar); + * controlState_RegisterCommand(state, control_Root); + * + * // this executes "root" + * controlState_DispatchCommand(state, "foo"); + * controlState_Destroy(&state); + * } + * @endcode + */ +void controlState_RegisterCommand(ControlState *state, CommandOps *command); + +/** + * Performs a longest-matching prefix of the args to the command tree + * + * The command tree is created with controlState_RegisterCommand. + * + * @param [in] state The allocated ControlState + * @param [in] args Each command_line word parsed to the ordered list + * + * @return CommandReturn_Success the command was successful + * @return CommandReturn_Failure the command failed or was not found + * @return CommandReturn_Exit the command indicates that the interactive mode + * should exit + * + * Example: + * @code + * <#example#> + * @endcode + */ +CommandReturn controlState_DispatchCommand(ControlState *state, PARCList *args); + +/** + * Begin an interactive shell + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +int controlState_Interactive(ControlState *state); + +/** + * Write then Read a command + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +struct iovec *controlState_WriteRead(ControlState *state, struct iovec *msg); + +/** + * Sets the Debug mode, which will print out much more information. + * + * Prints out much more diagnostic information about what hicn-light controller + * is doing. yes, you would make a CommandOps to set and unset this :) + * + * @param [in] debugFlag true means to print debug info, false means to turn it + * off + * + * Example: + * @code + * <#example#> + * @endcode + */ +void controlState_SetDebug(ControlState *state, bool debugFlag); + +/** + * Returns the debug state of ControlState + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool controlState_GetDebug(ControlState *state); +#endif // control_h + +void controlState_SetCommandOutput(ControlState *state, char **commandData); + +void controlState_ReleaseCommandOutput(ControlState *state, char **commandData, + size_t commandLenght); + +char **controlState_GetCommandOutput(ControlState *state); + +void controlState_SetInteractiveFlag(ControlState *state, bool interactive); + +bool controlState_IsInteractive(ControlState *state); + +void *controlState_GetUserdata(ControlState *state); + +bool controlState_isConfigFile(ControlState *state); + +int controlState_GetSockfd(ControlState *state); diff --git a/hicn-light/src/config/controlUnset.c b/hicn-light/src/config/controlUnset.c new file mode 100755 index 000000000..2da6a6518 --- /dev/null +++ b/hicn-light/src/config/controlUnset.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +static void _controlUnset_Init(CommandParser *parser, CommandOps *ops); + +static CommandReturn _controlUnset_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args); +static CommandReturn _controlUnset_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args); + +static const char *_commandUnset = "unset"; +static const char *_commandUnsetHelp = "help unset"; + +// =========================================================== + +CommandOps *controlUnset_Create(ControlState *state) { + return commandOps_Create(state, _commandUnset, _controlUnset_Init, + _controlUnset_Execute, commandOps_Destroy); +} + +CommandOps *controlUnset_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandUnsetHelp, NULL, + _controlUnset_HelpExecute, commandOps_Destroy); +} + +// =========================================================== + +static void _controlUnset_Init(CommandParser *parser, CommandOps *ops) { + ControlState *state = ops->closure; + controlState_RegisterCommand(state, controlUnsetDebug_Create(state)); + controlState_RegisterCommand(state, controlUnsetDebug_HelpCreate(state)); +} + +static CommandReturn _controlUnset_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + CommandOps *ops_help_unset_debug = controlUnsetDebug_HelpCreate(NULL); + + printf("Available commands:\n"); + printf(" %s\n", ops_help_unset_debug->command); + printf("\n"); + + commandOps_Destroy(&ops_help_unset_debug); + return CommandReturn_Success; +} + +static CommandReturn _controlUnset_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + return _controlUnset_HelpExecute(parser, ops, args); +} diff --git a/hicn-light/src/config/controlUnset.h b/hicn-light/src/config/controlUnset.h new file mode 100755 index 000000000..6eeb983f7 --- /dev/null +++ b/hicn-light/src/config/controlUnset.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_Unset.h + * @brief Implements the unset node of the CLI tree + * + * Implements the "unset" and "help unset" nodes of the command tree + * + */ +#ifndef Control_Unset_h +#define Control_Unset_h + +#include +CommandOps *controlUnset_Create(ControlState *state); +CommandOps *controlUnset_HelpCreate(ControlState *state); +#endif // Control_Unset_h diff --git a/hicn-light/src/config/controlUnsetDebug.c b/hicn-light/src/config/controlUnsetDebug.c new file mode 100755 index 000000000..4892bd513 --- /dev/null +++ b/hicn-light/src/config/controlUnsetDebug.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +static CommandReturn _controlUnsetDebug_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlUnsetDebug_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandUnsetDebug = "unset debug"; +static const char *_commandUnsetDebugHelp = "help unset debug"; + +// ==================================================== + +CommandOps *controlUnsetDebug_Create(ControlState *state) { + return commandOps_Create(state, _commandUnsetDebug, NULL, + _controlUnsetDebug_Execute, commandOps_Destroy); +} + +CommandOps *controlUnsetDebug_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandUnsetDebugHelp, NULL, + _controlUnsetDebug_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlUnsetDebug_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("unset debug: will disable the debug flag\n"); + printf("\n"); + return CommandReturn_Success; +} + +static CommandReturn _controlUnsetDebug_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 2) { + _controlUnsetDebug_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + ControlState *state = ops->closure; + controlState_SetDebug(state, false); + printf("Debug flag cleared\n\n"); + + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlUnsetDebug.h b/hicn-light/src/config/controlUnsetDebug.h new file mode 100755 index 000000000..e34f8aa5f --- /dev/null +++ b/hicn-light/src/config/controlUnsetDebug.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_UnsetDebug.h + * @brief Unsets the debug flag for more verbose output + * + * Implements the "unset debug" and "help unset debug" nodes of the CLI tree + * + */ + +#ifndef Control_UnsetDebug_h +#define Control_UnsetDebug_h + +#include +CommandOps *controlUnsetDebug_Create(ControlState *state); +CommandOps *controlUnsetDebug_HelpCreate(ControlState *state); +#endif // Control_UnsetDebug_h diff --git a/hicn-light/src/config/symbolicNameTable.c b/hicn-light/src/config/symbolicNameTable.c new file mode 100755 index 000000000..ccf416d67 --- /dev/null +++ b/hicn-light/src/config/symbolicNameTable.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +struct symblic_name_table { + PARCHashCodeTable *symbolicNameTable; + PARCHashCodeTable *indexToNameTable; +}; + +// ======================================================================================== +// symbolic name table functions + +static bool _symbolicNameEquals(const void *keyA, const void *keyB) { + return (strcasecmp((const char *)keyA, (const char *)keyB) == 0); +} + +static HashCodeType _symbolicNameHash(const void *keyA) { + const char *str = (const char *)keyA; + size_t length = strlen(str); + return parcHash32_Data(str, length); +} + +static bool _connectionIdEquals(const void *keyA, const void *keyB) { + unsigned idA = *((unsigned *)keyA); + unsigned idB = *((unsigned *)keyB); + return (idA == idB); +} + +static HashCodeType _connectionIdHash(const void *keyA) { + unsigned idA = *((unsigned *)keyA); + return parcHash32_Int32(idA); +} + +// ======================================================================================== + +SymbolicNameTable *symbolicNameTable_Create(void) { + SymbolicNameTable *table = parcMemory_Allocate(sizeof(SymbolicNameTable)); + + if (table) { + // key = char * + // value = uint32_t * + table->symbolicNameTable = parcHashCodeTable_Create( + _symbolicNameEquals, _symbolicNameHash, parcMemory_DeallocateImpl, + parcMemory_DeallocateImpl); + table->indexToNameTable = parcHashCodeTable_Create( + _connectionIdEquals, _connectionIdHash, parcMemory_DeallocateImpl, + parcMemory_DeallocateImpl); + } + + return table; +} + +void symbolicNameTable_Destroy(SymbolicNameTable **tablePtr) { + SymbolicNameTable *table = *tablePtr; + parcHashCodeTable_Destroy(&table->symbolicNameTable); + // parcHashCodeTable_Destroy(&table->indexToNameTable); + parcMemory_Deallocate((void **)&table); + *tablePtr = NULL; +} + +static char *_createKey(const char *symbolicName) { + char *key = parcMemory_StringDuplicate(symbolicName, strlen(symbolicName)); + + // convert key to upper case + char *p = key; + + // keeps looping until the first null + while ((*p = toupper(*p))) { + p++; + } + return key; +} + +bool symbolicNameTable_Exists(SymbolicNameTable *table, + const char *symbolicName) { + parcAssertNotNull(table, "Parameter table must be non-null"); + parcAssertNotNull(symbolicName, "Parameter symbolicName must be non-null"); + + char *key = _createKey(symbolicName); + bool found = (parcHashCodeTable_Get(table->symbolicNameTable, key) != NULL); + parcMemory_Deallocate((void **)&key); + return found; +} + +void symbolicNameTable_Remove(SymbolicNameTable *table, + const char *symbolicName) { + parcAssertNotNull(table, "Parameter table must be non-null"); + parcAssertNotNull(symbolicName, "Parameter symbolicName must be non-null"); + + char *key = _createKey(symbolicName); + + unsigned id = symbolicNameTable_Get(table, symbolicName); + uint32_t *value = parcMemory_Allocate(sizeof(uint32_t)); + *value = id; + + parcHashCodeTable_Del(table->symbolicNameTable, key); + parcHashCodeTable_Del(table->indexToNameTable, value); + parcMemory_Deallocate((void **)&key); + parcMemory_Deallocate((void **)&value); +} + +bool symbolicNameTable_Add(SymbolicNameTable *table, const char *symbolicName, + unsigned connid) { + parcAssertNotNull(table, "Parameter table must be non-null"); + parcAssertNotNull(symbolicName, "Parameter symbolicName must be non-null"); + parcAssertTrue(connid < UINT32_MAX, "Parameter connid must be less than %u", + UINT32_MAX); + + char *key = _createKey(symbolicName); + + uint32_t *value = parcMemory_Allocate(sizeof(uint32_t)); + *value = connid; + + bool success = parcHashCodeTable_Add(table->symbolicNameTable, key, value); + success = parcHashCodeTable_Add(table->indexToNameTable, value, key); + if (!success) { + parcMemory_Deallocate((void **)&key); + parcMemory_Deallocate((void **)&value); + } + + return success; +} + +unsigned symbolicNameTable_Get(SymbolicNameTable *table, + const char *symbolicName) { + parcAssertNotNull(table, "Parameter table must be non-null"); + parcAssertNotNull(symbolicName, "Parameter symbolicName must be non-null"); + + unsigned connid = UINT32_MAX; + + char *key = _createKey(symbolicName); + + uint32_t *value = parcHashCodeTable_Get(table->symbolicNameTable, key); + if (value) { + connid = *value; + } + + parcMemory_Deallocate((void **)&key); + return connid; +} + +const char *symbolicNameTable_GetNameByIndex(SymbolicNameTable *table, + unsigned id) { + parcAssertNotNull(table, "Parameter table must be non-null"); + + uint32_t *value = parcMemory_Allocate(sizeof(uint32_t)); + *value = id; + + const char *name = parcHashCodeTable_Get(table->indexToNameTable, value); + if (name == NULL) name = ""; + + parcMemory_Deallocate((void **)&value); + return name; +} diff --git a/hicn-light/src/config/symbolicNameTable.h b/hicn-light/src/config/symbolicNameTable.h new file mode 100755 index 000000000..69919cf00 --- /dev/null +++ b/hicn-light/src/config/symbolicNameTable.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file symbolicNameTable.h + * @brief The symbolic name table maps a string name to a connection id + * + * When configuring tunnels/connections, the user provides a string name + * (symbolic name) that they will use to refer to that connection. The symblic + * name table translates that symbolic name to a connection id. + * + */ + +#ifndef symbolicNameTable_h +#define symbolicNameTable_h + +struct symblic_name_table; +typedef struct symblic_name_table SymbolicNameTable; + +#include + +/** + * Creates a symbolic name table + * + * Allocates a SymbolicNameTable, which will store the symbolic names + * in a hash table. + * + * @retval non-null An allocated SymbolicNameTable + * @retval null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +SymbolicNameTable *symbolicNameTable_Create(void); + +/** + * Destroys a name table + * + * All keys and data are released. + * + * @param [in,out] tablePtr A pointer to a SymbolicNameTable, which will be + * NULL'd + * + * Example: + * @code + * <#example#> + * @endcode + */ +void symbolicNameTable_Destroy(SymbolicNameTable **tablePtr); + +/** + * Checks if the name (case insensitive) is in the table + * + * Does a case-insensitive match to see if the name is in the table + * + * @param [in] table An allocated SymbolicNameTable + * @param [in] symbolicName The name to check for + * + * @retval true The name is in the table + * @retval false The name is not in the talbe + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool symbolicNameTable_Exists(SymbolicNameTable *table, + const char *symbolicName); + +/** + * Adds a (name, connid) pair to the table. + * + * The name is stored case insensitive. The value UINT_MAX is used to indicate + * a non-existent key, so it should not be stored as a value in the table. + * + * @param [in] table An allocated SymbolicNameTable + * @param [in] symbolicName The name to save (will make a copy) + * @param [in] connid The connection id to associate with the name + * + * @retval true The pair was added + * @retval false The pair was not added (likely duplicate key) + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool symbolicNameTable_Add(SymbolicNameTable *table, const char *symbolicName, + unsigned connid); + +/** + * Returns the connection id associated with the symbolic name + * + * This function will look for the given name (case insensitive) and return the + * corresponding connid. If the name is not in the table, the function will + * return UINT_MAX. + * + * @param [in] table An allocated SymbolicNameTable + * @param [in] symbolicName The name to retrieve + * + * @retval UINT_MAX symbolicName not found + * @retval number the corresponding connid. + * + * Example: + * @code + * <#example#> + * @endcode + */ +unsigned symbolicNameTable_Get(SymbolicNameTable *table, + const char *symbolicName); + +void symbolicNameTable_Remove(SymbolicNameTable *table, + const char *symbolicName); +const char *symbolicNameTable_GetNameByIndex(SymbolicNameTable *table, + unsigned id); + +#endif /* defined(symbolicNameTable_h) */ diff --git a/hicn-light/src/content_store/CMakeLists.txt b/hicn-light/src/content_store/CMakeLists.txt new file mode 100755 index 000000000..85643cf5e --- /dev/null +++ b/hicn-light/src/content_store/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/contentStoreEntry.h + ${CMAKE_CURRENT_SOURCE_DIR}/contentStoreInterface.h + ${CMAKE_CURRENT_SOURCE_DIR}/contentStoreLRU.h + ${CMAKE_CURRENT_SOURCE_DIR}/listTimeOrdered.h + ${CMAKE_CURRENT_SOURCE_DIR}/listLRU.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/contentStoreInterface.c + ${CMAKE_CURRENT_SOURCE_DIR}/contentStoreLRU.c + ${CMAKE_CURRENT_SOURCE_DIR}/listLRU.c + ${CMAKE_CURRENT_SOURCE_DIR}/listTimeOrdered.c + ${CMAKE_CURRENT_SOURCE_DIR}/contentStoreEntry.c +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/hicn-light/src/content_store/contentStoreEntry.c b/hicn-light/src/content_store/contentStoreEntry.c new file mode 100755 index 000000000..d36ed61a0 --- /dev/null +++ b/hicn-light/src/content_store/contentStoreEntry.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +#include + +const uint64_t contentStoreEntry_MaxExpiryTime = UINT64_MAX; + +struct contentstore_entry { + Message *message; + ListLruEntry *lruEntry; + unsigned refcount; + bool hasExpiryTimeTicks; + uint64_t expiryTimeTicks; +}; + +ContentStoreEntry *contentStoreEntry_Create(Message *contentMessage, + ListLru *listLRU) { + parcAssertNotNull(contentMessage, "Parameter objectMessage must be non-null"); + + ContentStoreEntry *entry = + parcMemory_AllocateAndClear(sizeof(ContentStoreEntry)); + parcAssertNotNull(entry, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ContentStoreEntry)); + entry->message = message_Acquire(contentMessage); + entry->refcount = 1; + + if (listLRU != NULL) { + entry->lruEntry = listLRU_NewHeadEntry(listLRU, entry); + } + + entry->hasExpiryTimeTicks = message_HasContentExpiryTime(contentMessage); + + if (entry->hasExpiryTimeTicks) { + entry->expiryTimeTicks = message_GetContentExpiryTimeTicks(contentMessage); + } + + return entry; +} + +ContentStoreEntry *contentStoreEntry_Acquire( + const ContentStoreEntry *original) { + parcAssertNotNull(original, "Parameter must be non-null"); + ((ContentStoreEntry *)original)->refcount++; + return (ContentStoreEntry *)original; +} + +void contentStoreEntry_Release(ContentStoreEntry **entryPtr) { + parcAssertNotNull(entryPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*entryPtr, + "Parameter must dereference to non-null pointer"); + + ContentStoreEntry *entry = *entryPtr; + parcAssertTrue(entry->refcount > 0, "Illegal state: has refcount of 0"); + + entry->refcount--; + if (entry->refcount == 0) { + if (entry->lruEntry) { + listLRU_EntryDestroy(&entry->lruEntry); + } + message_Release(&entry->message); + parcMemory_Deallocate((void **)&entry); + } + *entryPtr = NULL; +} + +Message *contentStoreEntry_GetMessage(const ContentStoreEntry *storeEntry) { + parcAssertNotNull(storeEntry, "Parameter must be non-null"); + return storeEntry->message; +} + +bool contentStoreEntry_HasExpiryTimeTicks(const ContentStoreEntry *storeEntry) { + parcAssertNotNull(storeEntry, "Parameter must be non-null"); + return storeEntry->hasExpiryTimeTicks; +} + +uint64_t contentStoreEntry_GetExpiryTimeTicks( + const ContentStoreEntry *storeEntry) { + parcAssertNotNull(storeEntry, "Parameter must be non-null"); + parcAssertTrue(storeEntry->hasExpiryTimeTicks, + "storeEntry has no ExpiryTimeTicks. Did you call " + "contentStoreEntry_HasExpiryTimeTicks() first?"); + return storeEntry->expiryTimeTicks; +} + +int contentStoreEntry_CompareExpiryTime(const ContentStoreEntry *value1, + const ContentStoreEntry *value2) { + // A signum comparison. negative if key 1 is smaller, 0 if key1 == key2, + // greater than 0 if key1 is bigger. + + ContentStoreEntry *v1 = (ContentStoreEntry *)value1; + ContentStoreEntry *v2 = (ContentStoreEntry *)value2; + + if (v1->expiryTimeTicks < v2->expiryTimeTicks) { + return -1; + } else if (v1->expiryTimeTicks > v2->expiryTimeTicks) { + return +1; + } else { + // At this point, the times are the same. Use the address of the message as + // the decider. This allows us to store multiple messages with the same + // expiry/cache time. + if (v1->message < v2->message) { + return -1; + } else if (v1->message > v2->message) { + return +1; + } + } + + return 0; // The same message has been encountered. +} + +void contentStoreEntry_MoveToHead(ContentStoreEntry *storeEntry) { + parcAssertNotNull(storeEntry, "Parameter must be non-null"); + parcAssertNotNull(storeEntry->lruEntry, + "ContentStoreEntry is not attached to an ListLru"); + if (storeEntry->lruEntry) { + listLRU_EntryMoveToHead(storeEntry->lruEntry); + } +} diff --git a/hicn-light/src/content_store/contentStoreEntry.h b/hicn-light/src/content_store/contentStoreEntry.h new file mode 100755 index 000000000..766cc15e4 --- /dev/null +++ b/hicn-light/src/content_store/contentStoreEntry.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef contentStoreEntry_h +#define contentStoreEntry_h + +#include +#include + +struct contentstore_entry; +typedef struct contentstore_entry ContentStoreEntry; + +/** + * The max time allowed for an ExpiryTime. Will never be exceeded. + */ +extern const uint64_t contentStoreEntry_MaxExpiryTime; + +/** + * Creates a new `ContentStoreEntry` instance, acquiring a reference to the + * supplied `Message`. + * + * @param message the message to store + * @param listLRU the LRU list that this entry will be stored in. + * @return A newly created `ContentStoreEntry` instance that must eventually be + * released by calling + * {@link contentStoreEntry_Release}. + * + * @see contentStoreEntry_Release + */ +ContentStoreEntry *contentStoreEntry_Create(Message *objectMessage, + ListLru *listLRU); + +/** + * Returns a reference counted copy of the supplied `ContentStoreEntry`. + * + * @param original the ContentStoreEntry to return a reference to. + * @return Reference counted copy, must call + * contentStoreEntry_Destroy() on it. + */ +ContentStoreEntry *contentStoreEntry_Acquire(const ContentStoreEntry *original); + +/** + * Releases one reference count and destroys object when reaches zero + * + * @param [in,out] entryPtr A pointer to an allocated ContentStoreEntry + * + */ +void contentStoreEntry_Release(ContentStoreEntry **entryPtr); + +/** + * Returns a pointer to the contained {@link Message}. + * The caller must called {@link message_Acquire()} if they want to keep a + * reference to the returned message. + * + * @param storeEntry the ContentStoreEntry from which to retrieve the `Message` + * pointer. + * @return the address of the `Message` contained in the storeEntry. + * @see message_Acquire + */ +Message *contentStoreEntry_GetMessage(const ContentStoreEntry *storeEntry); + +/** + * Return true if the message stored in this `ContentStoreEntry` has an + * ExpiryTime. + * + * @param storeEntry the ContentStoreEntry containing the message. + * @return true if the referenced message has an ExpiryTime. False, otherwise. + */ +bool contentStoreEntry_HasExpiryTimeTicks(const ContentStoreEntry *storeEntry); + +/** + * Return the ExpiryTime stored in this `ContentStoreEntry`. + * + * @param storeEntry the ContentStoreEntry from which to retrieve the `Message` + * pointer. + * @return the address of the `Message` contained in the storeEntry. + */ +uint64_t contentStoreEntry_GetExpiryTimeTicks( + const ContentStoreEntry *storeEntry); + +/** + * A signum function comparing two `ContentStoreEntry` instances, using their + * ExpiryTime and, if necessary, the addresses of the referenced Message. In + * other words, if two ContentStoreEntries have the same ExpiryTime, the + * comparison will then be made on the memory addresses of the Messages + * referenced by the ContentStoreEntrys. So, the only way two ContentStoreEntrys + * will compare equally (0) is if they both have the same ExpiryTime and + * reference the same Message. + * + * Used to determine the ordering relationship of two `ContentStoreEntry` + * instances. This is used by the {@link ListTimeOrdered} to keep a list of + * ContentStoreEntrys, sorted by ExpiryTime. + * + * @param [in] storeEntry1 A pointer to a `ContentStoreEntry` instance. + * @param [in] storeEntry2 A pointer to a `ContentStoreEntry` instance to be + * compared to `storeEntry1`. + * + * @return 0 if `storeEntry1` and `storeEntry2` are equivalent + * @return < 0 if `storeEntry1` < `storeEntry2` + * @return > 0 if `storeEntry1` > `storeEntry2` + * + * Example: + * @code + * { + * ContentStoreEntry *entry1 = contentStoreEntry_Create(...); + * ContentStoreEntry *entry2 = contentStoreEntry_Create(...); + * + * int val = contentStoreEntry_CompareExpiryTime(entry1, entry2); + * if (val < 0) { + * // entry1 has a lower ExpiryTime, or the same ExpiryTime as entry2 + * and a different message. } else if (val > 0) { + * // entry2 has a lower ExpiryTime, or the same ExpiryTime as entry1 + * and a different message. } else { + * // entry1 and entry2 have the same ExpiryTime AND the same message. + * } + * + * contentStoreEntry_Release(&entry1); + * contentStoreEntry_Release(&entry2); + * + * } + * @endcode + */ +int contentStoreEntry_CompareExpiryTime(const ContentStoreEntry *storeEntry1, + const ContentStoreEntry *storeEntry2); + +/** + * Move this entry to the head of the LRU list + * + * Moves the entry to the head of the LRU list it was created with + * + * @param [in] storeEntry An allocated ContenstoreEntry + */ +void contentStoreEntry_MoveToHead(ContentStoreEntry *storeEntry); +#endif // contentStoreEntry_h diff --git a/hicn-light/src/content_store/contentStoreInterface.c b/hicn-light/src/content_store/contentStoreInterface.c new file mode 100755 index 000000000..a42041670 --- /dev/null +++ b/hicn-light/src/content_store/contentStoreInterface.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +void contentStoreInterface_Release(ContentStoreInterface **storeImplPtr) { + (*storeImplPtr)->release(storeImplPtr); +} + +bool contentStoreInterface_PutContent(ContentStoreInterface *storeImpl, + Message *content, + uint64_t currentTimeTicks) { + return storeImpl->putContent(storeImpl, content, currentTimeTicks); +} + +bool contentStoreInterface_RemoveContent(ContentStoreInterface *storeImpl, + Message *content) { + return storeImpl->removeContent(storeImpl, content); +} + +Message *contentStoreInterface_MatchInterest(ContentStoreInterface *storeImpl, + Message *interest, + uint64_t currentTimeTicks) { + return storeImpl->matchInterest(storeImpl, interest, currentTimeTicks); +} + +size_t contentStoreInterface_GetObjectCapacity( + ContentStoreInterface *storeImpl) { + return storeImpl->getObjectCapacity(storeImpl); +} + +size_t contentStoreInterface_GetObjectCount(ContentStoreInterface *storeImpl) { + return storeImpl->getObjectCount(storeImpl); +} + +void contentStoreInterface_Log(ContentStoreInterface *storeImpl) { + storeImpl->log(storeImpl); +} + +void *contentStoreInterface_GetPrivateData(ContentStoreInterface *storeImpl) { + return storeImpl->_privateData; +} diff --git a/hicn-light/src/content_store/contentStoreInterface.h b/hicn-light/src/content_store/contentStoreInterface.h new file mode 100755 index 000000000..d73c63019 --- /dev/null +++ b/hicn-light/src/content_store/contentStoreInterface.h @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef contentStoreInterface_h +#define contentStoreInterface_h + +#include + +#include + +typedef struct contentstore_config { + size_t objectCapacity; +} ContentStoreConfig; + +typedef struct contentstore_interface ContentStoreInterface; + +struct contentstore_interface { + /** + * Place a Message representing a ContentObject into the ContentStore. If + * necessary to make room, remove expired content or content that has exceeded + * the Recommended Cache Time. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + * @param content - a pointer to a `Message` to place in the store. + * @param currentTimeTicks - the current time, in hicn-light ticks, since the + * UTC epoch. + */ + bool (*putContent)(ContentStoreInterface *storeImpl, Message *content, + uint64_t currentTimeTicks); + + /** + * The function to call to remove content from the ContentStore. + * It will Release any references that were created when the content was + * placed into the ContentStore. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + * @param content - a pointer to a `Message` to remove from the store. + */ + bool (*removeContent)(ContentStoreInterface *storeImpl, Message *content); + + /** + * Given a Message that represents and Interest, try to find a matching + * ContentObject. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + * @param interest - a pointer to a `Message` representing the Interest to + * match. + * + * @return a pointer to a Message containing the matching ContentObject + * @return NULL if no matching ContentObject was found + */ + Message *(*matchInterest)(ContentStoreInterface *storeImpl, Message *interest, + uint64_t currentTimeTicks); + + /** + * Return the maximum number of ContentObjects that can be stored in this + * ContentStore. This is a raw count, not based on memory size. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + * + * @return the maximum number of ContentObjects that can be stored + */ + size_t (*getObjectCapacity)(ContentStoreInterface *storeImpl); + + /** + * Return the number of ContentObjects currently stored in the ContentStore. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + * + * @return the current number of ContentObjects in the ContentStore + */ + size_t (*getObjectCount)(ContentStoreInterface *storeImpl); + + /** + * Log a ContentStore implementation specific version of store-related + * information. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + */ + void (*log)(ContentStoreInterface *storeImpl); + + /** + * Acquire a new reference to the specified ContentStore instance. This + * reference will eventually need to be released by calling {@link + * contentStoreInterface_Release}. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + */ + ContentStoreInterface *(*acquire)(const ContentStoreInterface *storeImpl); + + /** + * Release the ContentStore, which will also Release any references held by + * it. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + */ + void (*release)(ContentStoreInterface **storeImpl); + + /** + * A pointer to opaque private data used by the ContentStore instance + * represented by this instance of ContentStoreInterface. + */ + void *_privateData; +}; + +/** + * Place a Message representing a ContentObject into the ContentStore. If + * necessary to make room, remove expired content or content that has exceeded + * the Recommended Cache Time. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + * @param content - a pointer to a `Message` to place in the store. + * + * @param currentTimeTicks - the current time, in hicn-light ticks, since the + * UTC epoch. + */ +bool contentStoreInterface_PutContent(ContentStoreInterface *storeImpl, + Message *content, + uint64_t currentTimeTicks); + +/** + * The function to call to remove content from the ContentStore. + * It will Release any references that were created when the content was placed + * into the ContentStore. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + * @param content - a pointer to a `Message` to remove from the store. + */ +bool contentStoreInterface_RemoveContent(ContentStoreInterface *storeImpl, + Message *content); + +/** + * Given a Message that represents and Interest, try to find a matching + * ContentObject. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + * @param interest - a pointer to a `Message` representing the Interest to + * match. + * + * @return a pointer to a Message containing the matching ContentObject + * @return NULL if no matching ContentObject was found + */ +Message *contentStoreInterface_MatchInterest(ContentStoreInterface *storeImpl, + Message *interest, + uint64_t currentTimeTicks); + +/** + * Return the maximum number of ContentObjects that can be stored in this + * ContentStore. This is a raw count, not based on memory size. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + * + * @return the maximum number of ContentObjects that can be stored + */ +size_t contentStoreInterface_GetObjectCapacity( + ContentStoreInterface *storeImpl); + +/** + * Return the number of ContentObjects currently stored in the ContentStore. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + * + * @return the current number of ContentObjects in the ContentStore + */ +size_t contentStoreInterface_GetObjectCount(ContentStoreInterface *storeImpl); + +/** + * Loga ContentStore implementation specific version of store-related + * information. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + */ +void contentStoreInterface_Log(ContentStoreInterface *storeImpl); + +/** + * Acquire a new reference to the specified ContentStore instance. This + * reference will eventually need to be released by calling {@link + * contentStoreInterface_Release}. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + */ +ContentStoreInterface *contentStoreInterface_Aquire( + const ContentStoreInterface *storeImpl); + +/** + * Release the ContentStore, which will also Release any references held by it. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + */ +void contentStoreInterface_Release(ContentStoreInterface **storeImplPtr); + +/** + * Return a pointer to the data private to this implementation of the + * ContentStore interface. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + */ +void *contentStoreInterface_GetPrivateData(ContentStoreInterface *storeImpl); +#endif // contentStoreInterface_h diff --git a/hicn-light/src/content_store/contentStoreLRU.c b/hicn-light/src/content_store/contentStoreLRU.c new file mode 100755 index 000000000..9b69d51c0 --- /dev/null +++ b/hicn-light/src/content_store/contentStoreLRU.c @@ -0,0 +1,455 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include +#include + +typedef struct contentstore_stats { + uint64_t countExpiryEvictions; + uint64_t countRCTEvictions; + uint64_t countLruEvictions; + uint64_t countAdds; + uint64_t countHits; + uint64_t countMisses; +} _ContentStoreLRUStats; + +typedef struct contentstore_lru_data { + size_t objectCapacity; + size_t objectCount; + + Logger *logger; + + // This LRU is just for keeping track of insertion and access order. + ListLru *lru; + + ListTimeOrdered *indexByExpirationTime; + + PARCHashCodeTable *storageByName; + + _ContentStoreLRUStats stats; +} _ContentStoreLRU; + +static void _destroyIndexes(_ContentStoreLRU *store) { + if (store->indexByExpirationTime != NULL) { + listTimeOrdered_Release(&(store->indexByExpirationTime)); + } + + if (store->storageByName != NULL) { + parcHashCodeTable_Destroy(&(store->storageByName)); + } + + if (store->lru != NULL) { + listLRU_Destroy(&(store->lru)); + } +} + +static void _contentStoreInterface_Destroy( + ContentStoreInterface **storeImplPtr) { + _ContentStoreLRU *store = contentStoreInterface_GetPrivateData(*storeImplPtr); + + parcObject_Release((PARCObject **)&store); +} + +static bool _contentStoreLRU_Destructor(_ContentStoreLRU **storePtr) { + _ContentStoreLRU *store = *storePtr; + + _destroyIndexes(store); + logger_Release(&store->logger); + + return true; +} + +parcObject_Override(_ContentStoreLRU, PARCObject, + .destructor = (PARCObjectDestructor *) + _contentStoreLRU_Destructor); + +parcObject_ExtendPARCObject(ContentStoreInterface, + _contentStoreInterface_Destroy, NULL, NULL, NULL, + NULL, NULL, NULL); + +static parcObject_ImplementAcquire(_contentStoreLRU, ContentStoreInterface); +static parcObject_ImplementRelease(_contentStoreLRU, ContentStoreInterface); + +static void _hashTableFunction_ContentStoreEntryDestroyer(void **dataPtr) { + contentStoreEntry_Release((ContentStoreEntry **)dataPtr); +} + +static bool _contentStoreLRU_Init(_ContentStoreLRU *store, + ContentStoreConfig *config, Logger *logger) { + bool result = false; + + store->logger = logger_Acquire(logger); + + size_t initialSize = config->objectCapacity * 2; + memset(&store->stats, 0, sizeof(_ContentStoreLRUStats)); + + store->objectCapacity = config->objectCapacity; + store->objectCount = 0; + + // initial size must be at least 1 or else the data structures break. + initialSize = (initialSize == 0) ? 1 : initialSize; + + store->indexByExpirationTime = listTimeOrdered_Create( + (TimeOrderList_KeyCompare *)contentStoreEntry_CompareExpiryTime); + + store->storageByName = parcHashCodeTable_Create_Size( + hashTableFunction_MessageNameEquals, + hashTableFunction_MessageNameHashCode, NULL, + _hashTableFunction_ContentStoreEntryDestroyer, initialSize); + + store->lru = listLRU_Create(); + + // If any of the index tables couldn't be allocated, we can't continue. + if ((store->indexByExpirationTime == NULL) || + (store->storageByName == NULL) || (store->lru == NULL)) { + if (logger_IsLoggable(store->logger, LoggerFacility_Processor, + PARCLogLevel_Error)) { + logger_Log(store->logger, LoggerFacility_Processor, PARCLogLevel_Error, + __func__, + "ContentStoreLRU could not be created. Could not allocate all " + "index tables.", + (void *)store, store->objectCapacity); + } + + _destroyIndexes(store); + result = false; + } else { + result = true; + } + return result; +} + +/** + * Remove a ContentStoreEntry from all tables and indices. + */ +static void _contentStoreLRU_PurgeStoreEntry(_ContentStoreLRU *store, + ContentStoreEntry *entryToPurge) { + if (contentStoreEntry_HasExpiryTimeTicks(entryToPurge)) { + listTimeOrdered_Remove(store->indexByExpirationTime, entryToPurge); + } + + Message *content = contentStoreEntry_GetMessage(entryToPurge); + + // This _Del call will call the Release/Destroy on the ContentStoreEntry, + // which will remove it from the LRU as well. + parcHashCodeTable_Del(store->storageByName, content); + + store->objectCount--; +} + +static bool _contentStoreLRU_RemoveLeastUsed(_ContentStoreLRU *store) { + bool result = false; + + if (store->objectCount > 0) { + ListLruEntry *lruEntry = listLRU_PopTail(store->lru); + ContentStoreEntry *storeEntry = + (ContentStoreEntry *)listLRU_EntryGetData(lruEntry); + + if (logger_IsLoggable(store->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log( + store->logger, LoggerFacility_Processor, PARCLogLevel_Debug, __func__, + "ContentStore %p evict message %p by LRU (LRU evictions %" PRIu64 ")", + (void *)store, (void *)contentStoreEntry_GetMessage(storeEntry), + store->stats.countLruEvictions); + } + + _contentStoreLRU_PurgeStoreEntry(store, storeEntry); + + result = true; + } + return result; +} + +static void _evictByStorePolicy(_ContentStoreLRU *store, + uint64_t currentTimeInTicks) { + // We need to make room. Here's the plan: + // 1) Check to see if anything has expired. If so, remove it and we're done. + // If not, 2) Remove the least recently used item. + + ContentStoreEntry *entry = + listTimeOrdered_GetOldest(store->indexByExpirationTime); + if (entry && contentStoreEntry_HasExpiryTimeTicks(entry) && + (currentTimeInTicks > contentStoreEntry_GetExpiryTimeTicks(entry))) { + // Found an expired entry. Remove it, and we're done. + + store->stats.countExpiryEvictions++; + if (logger_IsLoggable(store->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(store->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, + "ContentStore %p evict message %p by ExpiryTime (ExpiryTime " + "evictions %" PRIu64 ")", + (void *)store, (void *)contentStoreEntry_GetMessage(entry), + store->stats.countExpiryEvictions); + } + + _contentStoreLRU_PurgeStoreEntry(store, entry); + } else { + store->stats.countLruEvictions++; + _contentStoreLRU_RemoveLeastUsed(store); + } +} + +static bool _contentStoreLRU_PutContent(ContentStoreInterface *storeImpl, + Message *content, + uint64_t currentTimeTicks) + +{ + bool result = false; + _ContentStoreLRU *store = + (_ContentStoreLRU *)contentStoreInterface_GetPrivateData(storeImpl); + parcAssertNotNull(store, "Parameter store must be non-null"); + parcAssertNotNull(content, "Parameter objectMessage must be non-null"); + + parcAssertTrue(message_GetType(content) == MessagePacketType_ContentObject, + "Parameter objectMessage must be a Content Object"); + + if (store->objectCapacity == 0) { + return false; + } + + uint64_t expiryTimeTicks = contentStoreEntry_MaxExpiryTime; + + if (message_HasContentExpiryTime(content)) { + expiryTimeTicks = message_GetContentExpiryTimeTicks(content); + } + // Don't add anything that's already expired or has exceeded RCT. + if (currentTimeTicks >= expiryTimeTicks) { + return false; + } + + if (store->objectCount >= store->objectCapacity) { + // Store is full. Need to make room. + _evictByStorePolicy(store, currentTimeTicks); + } + + // And now add a new entry to the head of the LRU. + + ContentStoreEntry *entry = contentStoreEntry_Create(content, store->lru); + + if (entry != NULL) { + if (parcHashCodeTable_Add(store->storageByName, content, entry)) { + if (contentStoreEntry_HasExpiryTimeTicks(entry)) { + listTimeOrdered_Add(store->indexByExpirationTime, entry); + } + + store->objectCount++; + store->stats.countAdds++; + + if (logger_IsLoggable(store->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(store->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, + "ContentStoreLRU %p saved message %p (object count %" PRIu64 + ")", + (void *)store, (void *)content, store->objectCount); + } + + result = true; + } else { + // Free what we just created, but did not add. 'entry' has ownership of + // 'copy', and so will call _Release() on it + contentStoreEntry_Release(&entry); + + if (logger_IsLoggable(store->logger, LoggerFacility_Processor, + PARCLogLevel_Warning)) { + logger_Log(store->logger, LoggerFacility_Processor, + PARCLogLevel_Warning, __func__, + "ContentStoreLRU %p failed to add message %p to hash table", + (void *)store, (void *)content); + } + } + } + + return result; +} + +static Message *_contentStoreLRU_MatchInterest(ContentStoreInterface *storeImpl, + Message *interest, + uint64_t currentTimeTicks) { + Message *result = NULL; + + _ContentStoreLRU *store = + (_ContentStoreLRU *)contentStoreInterface_GetPrivateData(storeImpl); + + parcAssertNotNull(store, "Parameter store must be non-null"); + parcAssertNotNull(interest, "Parameter interestMessage must be non-null"); + parcAssertTrue(message_GetType(interest) == MessagePacketType_Interest, + "Parameter interestMessage must be an Interest"); + + PARCHashCodeTable *table; + table = store->storageByName; + + ContentStoreEntry *storeEntry = parcHashCodeTable_Get(table, interest); + + bool foundEntry = false; + + if (storeEntry) { + if (contentStoreEntry_HasExpiryTimeTicks(storeEntry) && + contentStoreEntry_GetExpiryTimeTicks(storeEntry) < currentTimeTicks) { + // the entry is expired, we can remove it + _contentStoreLRU_PurgeStoreEntry(store, storeEntry); + } else { + foundEntry = true; + } + } + + if (foundEntry) { + contentStoreEntry_MoveToHead(storeEntry); + result = contentStoreEntry_GetMessage(storeEntry); + + store->stats.countHits++; + + if (logger_IsLoggable(store->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(store->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, + "ContentStoreLRU %p matched interest %p (hits %" PRIu64 + ", misses %" PRIu64 ")", + (void *)store, (void *)interest, store->stats.countHits, + store->stats.countMisses); + } + } else { + store->stats.countMisses++; + + if (logger_IsLoggable(store->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(store->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, + "ContentStoreLRU %p missed interest %p (hits %" PRIu64 + ", misses %" PRIu64 ")", + (void *)store, (void *)interest, store->stats.countHits, + store->stats.countMisses); + } + } + + return result; +} + +static bool _contentStoreLRU_RemoveContent(ContentStoreInterface *storeImpl, + Message *content) { + bool result = false; + _ContentStoreLRU *store = + (_ContentStoreLRU *)contentStoreInterface_GetPrivateData(storeImpl); + + ContentStoreEntry *storeEntry = + parcHashCodeTable_Get(store->storageByName, content); + + if (storeEntry != NULL) { + _contentStoreLRU_PurgeStoreEntry(store, storeEntry); + result = true; + } + + return result; +} + +static void _contentStoreLRU_Log(ContentStoreInterface *storeImpl) { + _ContentStoreLRU *store = + (_ContentStoreLRU *)contentStoreInterface_GetPrivateData(storeImpl); + + logger_Log(store->logger, LoggerFacility_Processor, PARCLogLevel_All, + __func__, + "ContentStoreLRU @%p {count = %zu, capacity = %zu {" + "stats = @%p {adds = %" PRIu64 ", hits = %" PRIu64 + ", misses = %" PRIu64 ", LRUEvictons = %" PRIu64 + ", ExpiryEvictions = %" PRIu64 ", RCTEvictions = %" PRIu64 "} }", + store, store->objectCount, store->objectCapacity, &store->stats, + store->stats.countAdds, store->stats.countHits, + store->stats.countMisses, store->stats.countLruEvictions, + store->stats.countExpiryEvictions, store->stats.countRCTEvictions); +} + +static size_t _contentStoreLRU_GetObjectCapacity( + ContentStoreInterface *storeImpl) { + _ContentStoreLRU *store = + (_ContentStoreLRU *)contentStoreInterface_GetPrivateData(storeImpl); + return store->objectCapacity; +} + +static size_t _contentStoreLRU_GetObjectCount( + ContentStoreInterface *storeImpl) { + _ContentStoreLRU *store = + (_ContentStoreLRU *)contentStoreInterface_GetPrivateData(storeImpl); + return store->objectCount; +} + +static size_t _contentStoreLRU_SetObjectCapacity( + ContentStoreInterface *storeImpl, size_t newCapacity) { + _ContentStoreLRU *store = + (_ContentStoreLRU *)contentStoreInterface_GetPrivateData(storeImpl); + return store->objectCapacity = newCapacity; +} + +ContentStoreInterface *contentStoreLRU_Create(ContentStoreConfig *config, + Logger *logger) { + ContentStoreInterface *storeImpl = NULL; + + parcAssertNotNull(logger, "ContentStoreLRU requires a non-NULL logger"); + + storeImpl = parcObject_CreateAndClearInstance(ContentStoreInterface); + + if (storeImpl != NULL) { + storeImpl->_privateData = + parcObject_CreateAndClearInstance(_ContentStoreLRU); + + if (_contentStoreLRU_Init(storeImpl->_privateData, config, logger)) { + storeImpl->putContent = &_contentStoreLRU_PutContent; + storeImpl->removeContent = &_contentStoreLRU_RemoveContent; + + storeImpl->matchInterest = &_contentStoreLRU_MatchInterest; + + storeImpl->getObjectCount = &_contentStoreLRU_GetObjectCount; + storeImpl->getObjectCapacity = &_contentStoreLRU_GetObjectCapacity; + + storeImpl->log = &_contentStoreLRU_Log; + + storeImpl->acquire = &_contentStoreLRU_Acquire; + storeImpl->release = &_contentStoreLRU_Release; + + // Initialize from the config passed to us. + _contentStoreLRU_SetObjectCapacity(storeImpl, config->objectCapacity); + + if (logger_IsLoggable(logger, LoggerFacility_Processor, + PARCLogLevel_Info)) { + logger_Log(logger, LoggerFacility_Processor, PARCLogLevel_Info, + __func__, "ContentStoreLRU %p created with capacity %zu", + (void *)storeImpl, + contentStoreInterface_GetObjectCapacity(storeImpl)); + } + } + } else { + parcObject_Release((void **)&storeImpl); + } + + return storeImpl; +} diff --git a/hicn-light/src/content_store/contentStoreLRU.h b/hicn-light/src/content_store/contentStoreLRU.h new file mode 100755 index 000000000..3c0815ebd --- /dev/null +++ b/hicn-light/src/content_store/contentStoreLRU.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef contentStoreLRU_h +#define contentStoreLRU_h + +#include +#include +#include + +/** + * Create and Initialize an instance of contentStoreLRU. A newly allocated + * {@link ContentStoreInterface} object is initialized and returned. It must + * eventually be released by calling {@link contentStoreInterface_Release}. + * + * + * @param config An instance of `ContentStoreConfig`, specifying options to be + * applied by the underlying contentStoreLRU instance. + * @param logger An instance of a {@link Logger} to use for logging content + * store events. + * + * @return a newly created contentStoreLRU instance. + * + */ +ContentStoreInterface *contentStoreLRU_Create(ContentStoreConfig *config, + Logger *logger); +#endif // contentStoreLRU_h diff --git a/hicn-light/src/content_store/listLRU.c b/hicn-light/src/content_store/listLRU.c new file mode 100755 index 000000000..42b491d7c --- /dev/null +++ b/hicn-light/src/content_store/listLRU.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include +#include + +struct list_lru_entry { + void *userData; + + // always set to the list + ListLru *parentList; + + // indicates if the Entry is currently in the list + bool inList; + + TAILQ_ENTRY(list_lru_entry) list; +}; + +// this defines the TAILQ structure so we can access the tail pointer +TAILQ_HEAD(lru_s, list_lru_entry); + +struct list_lru { + struct lru_s head; + size_t itemsInList; +}; + +void listLRU_EntryDestroy(ListLruEntry **entryPtr) { + parcAssertNotNull(entryPtr, + "Parameter entryPtr must be non-null double pointer"); + + ListLruEntry *entry = *entryPtr; + if (entry->inList) { + TAILQ_REMOVE(&entry->parentList->head, entry, list); + parcAssertTrue( + entry->parentList->itemsInList > 0, + "Invalid state, removed entry from list, but itemsInList is 0"); + entry->parentList->itemsInList--; + } + + parcMemory_Deallocate((void **)&entry); + *entryPtr = NULL; +} + +void listLRU_EntryMoveToHead(ListLruEntry *entry) { + parcAssertNotNull(entry, "Parameter entry must be non-null"); + + TAILQ_REMOVE(&entry->parentList->head, entry, list); + TAILQ_INSERT_HEAD(&entry->parentList->head, entry, list); +} + +void *listLRU_EntryGetData(ListLruEntry *entry) { return entry->userData; } + +ListLru *listLRU_Create() { + ListLru *list = parcMemory_AllocateAndClear(sizeof(ListLru)); + parcAssertNotNull(list, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ListLru)); + list->itemsInList = 0; + TAILQ_INIT(&list->head); + return list; +} + +void listLRU_Destroy(ListLru **lruPtr) { + parcAssertNotNull(lruPtr, "Parameter lruPtr must be non-null double pointer"); + + ListLru *lru = *lruPtr; + + ListLruEntry *entry = TAILQ_FIRST(&lru->head); + while (entry != NULL) { + ListLruEntry *next = TAILQ_NEXT(entry, list); + listLRU_EntryDestroy(&entry); + entry = next; + } + + parcMemory_Deallocate((void **)&lru); + *lruPtr = NULL; +} + +ListLruEntry *listLRU_NewHeadEntry(ListLru *lru, void *data) { + parcAssertNotNull(lru, "Parameter lru must be non-null"); + parcAssertNotNull(data, "Parameter data must be non-null"); + + ListLruEntry *entry = parcMemory_AllocateAndClear(sizeof(ListLruEntry)); + parcAssertNotNull(entry, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ListLruEntry)); + entry->userData = data; + entry->parentList = lru; + entry->inList = true; + + TAILQ_INSERT_HEAD(&lru->head, entry, list); + lru->itemsInList++; + + return entry; +} + +ListLruEntry *listLRU_PopTail(ListLru *lru) { + parcAssertNotNull(lru, "Parameter lru must be non-null"); + + ListLruEntry *entry = TAILQ_LAST(&lru->head, lru_s); + + if (entry) { + parcAssertTrue( + lru->itemsInList > 0, + "Invalid state, removed entry from list, but itemsInList is 0"); + lru->itemsInList--; + TAILQ_REMOVE(&lru->head, entry, list); + entry->inList = false; + } + + return entry; +} + +size_t listLRU_Length(const ListLru *lru) { + parcAssertNotNull(lru, "Parameter lru must be non-null"); + return lru->itemsInList; +} diff --git a/hicn-light/src/content_store/listLRU.h b/hicn-light/src/content_store/listLRU.h new file mode 100755 index 000000000..75f698b61 --- /dev/null +++ b/hicn-light/src/content_store/listLRU.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file listLRU.h + * @brief Maintains an LRU for the content store + * + * An LRU list is make up of LRU entries. The entries are bound to the list. + * The user of the list is reponsible for knowing when there's too many things + * and wants to remove one. The LRU list will grow without bound otherwise. + * + * The LRU list is meant to be used as an auxiliary data structure, not the + * primary storage of data elements. + * + */ + +#ifndef listLRU_h +#define listLRU_h + +struct list_lru_entry; +typedef struct list_lru_entry ListLruEntry; + +struct list_lru; +typedef struct list_lru ListLru; + +/** + * @function lruEntry_Destroy + * @abstract Destroys and element. This will also remove it from the list. + */ +void listLRU_EntryDestroy(ListLruEntry **entryPtr); + +/** + * @function listLRU_EntryMoveToHead + * @abstract move an element to head + */ +void listLRU_EntryMoveToHead(ListLruEntry *entry); + +/** + * @function lruEntry_GetData + * @abstract Returns the user-supplied opaque data when the entry was created + */ +void *listLRU_EntryGetData(ListLruEntry *entry); + +/** + * @function listLRU_Create + * @abstract Creates a new Least-Recently-Used list + */ +ListLru *listLRU_Create(); + +/** + * @function listLRU_Destroy + * @abstract Destroys a list and frees all the elements in it + */ +void listLRU_Destroy(ListLru **listPtr); + +/** + * Returns the number of items in the list + * + * @param [in] lru An allocated ListLru + * @retval number The number of items in the LRU list + */ +size_t listLRU_Length(const ListLru *lru); + +/** + * @function listLRU_NewHeadEntry + * @abstract Creates a new entry for the list. It is inserted at the head of + * the list. + */ +ListLruEntry *listLRU_NewHeadEntry(ListLru *lru, void *data); + +/** + * @function listLRU_PopTail + * @abstract Removes the tail element from the list and returns it to the user + * @discussion + * Pops the tail element. The user should examine its data to destroy their + * tail object, then call LruEntry_Destroy() to free the + * LRU entry. + * + * @return The tail element, or NULL for an empty list + */ +ListLruEntry *listLRU_PopTail(ListLru *list); +#endif // listLRU_h diff --git a/hicn-light/src/content_store/listTimeOrdered.c b/hicn-light/src/content_store/listTimeOrdered.c new file mode 100755 index 000000000..44697d202 --- /dev/null +++ b/hicn-light/src/content_store/listTimeOrdered.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include + +/** + * A list of ContentStoreEntrys, kept in sorted order by time. The ordering is + * calculated by a key compare function (e.g. {@link TimeOrderList_KeyCompare}), + * passed in. + * + * This container does not hold references to the objects that it contains. In + * other words, it does not Acquire() the Messages that are placed in it. That + * reference count is managed by the owning ContentStore. This is purely an + * index, and provides an easy to way index Messages based on a specified time + * value. Typically, that would be the Expiration Time. + * + * It maintains a tree, sorted by the time values passed in to the Add() + * function. It does not manage capacity, and can grow uncontrollably if the + * owning ContentStore does not manage it. Items are indexed first by time, then + * address of the Message (just as a distringuishing attribute). This allows us + * to store multiple items with the same expiration time. + */ + +struct list_timeordered { + PARCTreeRedBlack *timeOrderedTree; +}; + +static void _finalRelease(ListTimeOrdered **listP) { + ListTimeOrdered *list = *listP; + parcTreeRedBlack_Destroy(&list->timeOrderedTree); +} + +parcObject_ExtendPARCObject(ListTimeOrdered, _finalRelease, NULL, NULL, NULL, + NULL, NULL, NULL); + +parcObject_ImplementAcquire(listTimeOrdered, ListTimeOrdered); + +parcObject_ImplementRelease(listTimeOrdered, ListTimeOrdered); + +ListTimeOrdered *listTimeOrdered_Create( + TimeOrderList_KeyCompare *keyCompareFunction) { + ListTimeOrdered *result = parcObject_CreateInstance(ListTimeOrdered); + if (NULL != result) { + result->timeOrderedTree = + parcTreeRedBlack_Create(keyCompareFunction, // keyCompare + NULL, // keyFree + NULL, // keyCopy + NULL, // valueEquals + NULL, // valueFree + NULL); // valueCopy + } + return result; +} + +void listTimeOrdered_Add(ListTimeOrdered *list, ContentStoreEntry *entry) { + parcTreeRedBlack_Insert(list->timeOrderedTree, entry, entry); +} + +ContentStoreEntry *listTimeOrdered_GetOldest(ListTimeOrdered *list) { + return parcTreeRedBlack_FirstKey(list->timeOrderedTree); +} + +bool listTimeOrdered_Remove(ListTimeOrdered *list, + ContentStoreEntry *storeEntry) { + bool result = false; + + ContentStoreEntry *entry = (ContentStoreEntry *)parcTreeRedBlack_Remove( + list->timeOrderedTree, storeEntry); + if (entry != NULL) { + result = true; + } + return result; +} + +size_t listTimeOrdered_Length(ListTimeOrdered *list) { + return (size_t)parcTreeRedBlack_Size(list->timeOrderedTree); +} diff --git a/hicn-light/src/content_store/listTimeOrdered.h b/hicn-light/src/content_store/listTimeOrdered.h new file mode 100755 index 000000000..b18bd16f7 --- /dev/null +++ b/hicn-light/src/content_store/listTimeOrdered.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef listTimeOrdered_h +#define listTimeOrdered_h + +#include +#include +#include +#include + +struct list_timeordered; +typedef struct list_timeordered ListTimeOrdered; + +/** + * A signum function that takes two instances of ContentStoreEntrys and + * returns a value based on their relative values. + */ +typedef PARCTreeRedBlack_KeyCompare TimeOrderList_KeyCompare; + +/** + * Create a new instance of `ListTimeOrdered` that will maintain the order of + * its list items using the supplied `keyCompareFunction`. + * + * The newly created `ListTimeOrdered` must eventually be released by calling + * {@link listTimeOrdered_Release}. + * + * @param keyCompareFunction the signum comparison function to use to sort + * stored items. + * @return a new instance of `TimeOrderList`. + * @return NULL if the new instance couldn't be created. + * + */ +ListTimeOrdered *listTimeOrdered_Create( + TimeOrderList_KeyCompare *keyCompareFunction); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + */ +void listTimeOrdered_Release(ListTimeOrdered **listP); + +/** + * Add a {@link ContentStoreEntry} instance to the specified list. Note that a + * new refernece to the specified `storeEntry` is not acquired. + * + * @param list the list instance into which to add the specified storeEntry. + * @param storeEntry the storeEntry instance to add. + * + */ +void listTimeOrdered_Add(ListTimeOrdered *list, ContentStoreEntry *storeEntry); + +/** + * Remove a {@link ContentStoreEntry} instance from the specified list. + * + * @param list the list instance from which to remove the specified storeEntry. + * @param storeEntry the storeEntry instance to remove. + * @return true if the removal was succesful. + * @return false if the removal was not succesful. + * + */ +bool listTimeOrdered_Remove(ListTimeOrdered *list, + ContentStoreEntry *storeEntry); + +/** + * Return the oldest {@link ContentStoreEntry} instance in this list. That is, + * the one with the smallest time value. + * + * @param list the list instance from which to retrieve the oldest storeEntry. + * @param the oldest `ContentStoreEntry` in the list + * @param NULL if no `ContentStoreEntry` was available. + * + */ +ContentStoreEntry *listTimeOrdered_GetOldest(ListTimeOrdered *list); + +/** + * Return the number of items currently stored in the list. + * + * @param list the `ListTimeOrdered` instance from which to retrieve the count. + * @return the number of items in the list. + * + */ +size_t listTimeOrdered_Length(ListTimeOrdered *list); +#endif /* defined(listTimeOrdered_h) */ diff --git a/hicn-light/src/core/CMakeLists.txt b/hicn-light/src/core/CMakeLists.txt new file mode 100755 index 000000000..1d7dc03e9 --- /dev/null +++ b/hicn-light/src/core/CMakeLists.txt @@ -0,0 +1,55 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/connectionManager.h + ${CMAKE_CURRENT_SOURCE_DIR}/ticks.h + ${CMAKE_CURRENT_SOURCE_DIR}/connectionList.h + ${CMAKE_CURRENT_SOURCE_DIR}/connectionTable.h + ${CMAKE_CURRENT_SOURCE_DIR}/connection.h + ${CMAKE_CURRENT_SOURCE_DIR}/forwarder.h + ${CMAKE_CURRENT_SOURCE_DIR}/logger.h + ${CMAKE_CURRENT_SOURCE_DIR}/dispatcher.h + ${CMAKE_CURRENT_SOURCE_DIR}/message.h + ${CMAKE_CURRENT_SOURCE_DIR}/messagePacketType.h + ${CMAKE_CURRENT_SOURCE_DIR}/numberSet.h + ${CMAKE_CURRENT_SOURCE_DIR}/streamBuffer.h + ${CMAKE_CURRENT_SOURCE_DIR}/system.h + ${CMAKE_CURRENT_SOURCE_DIR}/mapMe.h + ${CMAKE_CURRENT_SOURCE_DIR}/wldr.h + ${CMAKE_CURRENT_SOURCE_DIR}/messageHandler.h + ${CMAKE_CURRENT_SOURCE_DIR}/nameBitvector.h + ${CMAKE_CURRENT_SOURCE_DIR}/name.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/connection.c + ${CMAKE_CURRENT_SOURCE_DIR}/connectionList.c + ${CMAKE_CURRENT_SOURCE_DIR}/connectionManager.c + ${CMAKE_CURRENT_SOURCE_DIR}/connectionTable.c + ${CMAKE_CURRENT_SOURCE_DIR}/dispatcher.c + ${CMAKE_CURRENT_SOURCE_DIR}/forwarder.c + ${CMAKE_CURRENT_SOURCE_DIR}/logger.c + ${CMAKE_CURRENT_SOURCE_DIR}/message.c + ${CMAKE_CURRENT_SOURCE_DIR}/numberSet.c + ${CMAKE_CURRENT_SOURCE_DIR}/streamBuffer.c + ${CMAKE_CURRENT_SOURCE_DIR}/mapMe.c + ${CMAKE_CURRENT_SOURCE_DIR}/wldr.c + ${CMAKE_CURRENT_SOURCE_DIR}/nameBitvector.c + ${CMAKE_CURRENT_SOURCE_DIR}/name.c +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/hicn-light/src/core/connection.c b/hicn-light/src/core/connection.c new file mode 100755 index 000000000..073b7260f --- /dev/null +++ b/hicn-light/src/core/connection.c @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +struct connection { + const AddressPair *addressPair; + IoOperations *ops; + + unsigned refCount; + + bool probing_active; + unsigned probing_interval; + unsigned counter; + Ticks last_sent; + Ticks delay; + + bool wldrAutoStart; // if true, wldr can be set automatically + // by default this value is set to true. + // if wldr is activated using a command (config + // file/hicnLightControl) this value is set to false so + // that a base station can not disable wldr at the client + Wldr *wldr; +}; + +Connection *connection_Create(IoOperations *ops) { + parcAssertNotNull(ops, "Parameter ops must be non-null"); + Connection *conn = parcMemory_AllocateAndClear(sizeof(Connection)); + parcAssertNotNull(conn, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Connection)); + conn->addressPair = ioOperations_GetAddressPair(ops); + conn->ops = ops; + conn->refCount = 1; + conn->wldr = NULL; + conn->probing_active = false; + + conn->wldrAutoStart = true; + conn->probing_interval = 0; + conn->counter = 0; + conn->last_sent = 0; + conn->delay = INT_MAX; + return conn; +} + +Connection *connection_Acquire(Connection *connection) { + parcAssertNotNull(connection, "Parameter conn must be non-null"); + connection->refCount++; + return connection; +} + +void connection_Release(Connection **connectionPtr) { + parcAssertNotNull(connectionPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*connectionPtr, + "Parameter must dereference to non-null pointer"); + Connection *conn = *connectionPtr; + + parcAssertTrue( + conn->refCount > 0, + "Invalid state, connection reference count should be positive, got 0."); + conn->refCount--; + if (conn->refCount == 0) { + // don't destroy addressPair, its part of ops. + ioOperations_Release(&conn->ops); + if (conn->wldr != NULL) { + wldr_Destroy(&(conn->wldr)); + } + parcMemory_Deallocate((void **)&conn); + } + *connectionPtr = NULL; +} + +bool connection_Send(const Connection *conn, Message *message) { + parcAssertNotNull(conn, "Parameter conn must be non-null"); + parcAssertNotNull(message, "Parameter message must be non-null"); + + if (ioOperations_IsUp(conn->ops)) { + if (message_GetType(message) == MessagePacketType_ContentObject) { + uint8_t connectionId = (uint8_t)connection_GetConnectionId(conn); + message_UpdatePathLabel(message, connectionId); + } + if (conn->wldr != NULL) { + wldr_SetLabel(conn->wldr, message); + } else { + message_ResetWldrLabel(message); + } + return ioOperations_Send(conn->ops, NULL, message); + } + return false; +} + +static void _sendProbe(Connection *conn, unsigned probeType, uint8_t *message) { + parcAssertNotNull(conn, "Parameter conn must be non-null"); + + if (probeType == PACKET_TYPE_PROBE_REQUEST) { + Ticks now = ioOperations_SendProbe(conn->ops, probeType, message); + if (now != 0) { + conn->last_sent = now; + } + } else { + ioOperations_SendProbe(conn->ops, probeType, message); + } +} + +void connection_Probe(Connection *conn) { + _sendProbe(conn, PACKET_TYPE_PROBE_REQUEST, NULL); +} + +void connection_HandleProbe(Connection *conn, uint8_t *probe, + Ticks actualTime) { + parcAssertNotNull(conn, "Parameter conn must be non-null"); + parcAssertNotNull(probe, "Parameter pkt must be non-null"); + + uint8_t probeType = messageHandler_GetProbePacketType(probe); + if (probeType == PACKET_TYPE_PROBE_REQUEST) { + _sendProbe(conn, PACKET_TYPE_PROBE_REPLY, probe); + } else if (probeType == PACKET_TYPE_PROBE_REPLY) { + Ticks delay = actualTime - conn->last_sent; + if (delay == 0) { + delay = 1; + } + if (delay < conn->delay) { + conn->delay = delay; + } + } else { + printf("receivde unkwon probe type\n"); + } +} + +uint64_t connection_GetDelay(Connection *conn) { return (uint64_t)conn->delay; } + +IoOperations *connection_GetIoOperations(const Connection *conn) { + return conn->ops; +} + +unsigned connection_GetConnectionId(const Connection *conn) { + parcAssertNotNull(conn, "Parameter conn must be non-null"); + return ioOperations_GetConnectionId(conn->ops); +} + +const AddressPair *connection_GetAddressPair(const Connection *conn) { + parcAssertNotNull(conn, "Parameter conn must be non-null"); + return ioOperations_GetAddressPair(conn->ops); +} + +bool connection_IsUp(const Connection *conn) { + parcAssertNotNull(conn, "Parameter conn must be non-null"); + if (!conn->ops) return false; + return ioOperations_IsUp(conn->ops); +} + +bool connection_IsLocal(const Connection *conn) { + parcAssertNotNull(conn, "Parameter conn must be non-null"); + return ioOperations_IsLocal(conn->ops); +} + +const void *connection_Class(const Connection *conn) { + parcAssertNotNull(conn, "Parameter conn must be non-null"); + return ioOperations_Class(conn->ops); +} + +bool connection_ReSend(const Connection *conn, Message *message, + bool notification) { + parcAssertNotNull(conn, "Parameter conn must be non-null"); + parcAssertNotNull(message, "Parameter message must be non-null"); + bool res = false; + + if (connection_IsUp(conn)) { + // here the wldr header is alreay set: this message is a retransmission or a + // notification + + // we need to recompiute the path lable since we always store a pointer to + // the same message if this message will be sent again to someonelse, the new + // path label must be computed starting from the orignal labelorignal label. + // Notice that we heve the same problem in case of PIT aggregation. That case + // is handled insied the MessageProcessor. This is specific to WLDR + // retransmittions. This is done only for data packets + + if (message_GetType(message) == MessagePacketType_ContentObject) { + uint8_t connectionId = (uint8_t)connection_GetConnectionId(conn); + uint32_t old_path_label = message_GetPathLabel(message); + message_UpdatePathLabel(message, connectionId); + + res = ioOperations_Send(conn->ops, NULL, message); + + message_SetPathLabel(message, old_path_label); + } else { + res = ioOperations_Send(conn->ops, NULL, message); + } + } + + if (notification) { + // the notification is never destroyed + message_Release(&message); + } + + return res; +} + +void connection_AllowWldrAutoStart(Connection *conn, bool allow) { + conn->wldrAutoStart = allow; +} + +void connection_EnableWldr(Connection *conn) { + if (!connection_IsLocal(conn)) { + if (conn->wldr == NULL) { + printf("----------------- enable wldr\n"); + conn->wldr = wldr_Init(); + } + } +} + +void connection_DisableWldr(Connection *conn) { + if (!connection_IsLocal(conn)) { + if (conn->wldr != NULL) { + printf("----------------- disable wldr\n"); + wldr_Destroy(&(conn->wldr)); + conn->wldr = NULL; + } + } +} + +bool connection_HasWldr(const Connection *conn) { + if (conn->wldr == NULL) { + return false; + } else { + return true; + } +} + +bool connection_WldrAutoStartAllowed(const Connection *conn) { + return conn->wldrAutoStart; +} + +void connection_DetectLosses(Connection *conn, Message *message) { + if (conn->wldr != NULL) wldr_DetectLosses(conn->wldr, conn, message); +} + +void connection_HandleWldrNotification(Connection *conn, Message *message) { + if (conn->wldr != NULL) + wldr_HandleWldrNotification(conn->wldr, conn, message); +} diff --git a/hicn-light/src/core/connection.h b/hicn-light/src/core/connection.h new file mode 100755 index 000000000..b5c703527 --- /dev/null +++ b/hicn-light/src/core/connection.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file connection.h + * @brief Wrapper for different types of connections + * + * A connection wraps a specific set of {@link IoOperations}. Those operations + * allow for input and output. Connections get stored in the Connection Table. + * + */ + +#ifndef connection_h +#define connection_h +#include +#include +#include + +// packet types for probing +#define PACKET_TYPE_PROBE_REQUEST 5 +#define PACKET_TYPE_PROBE_REPLY 6 + +struct connection; +typedef struct connection Connection; + +/** + * Creates a connection object. + */ +Connection *connection_Create(IoOperations *ops); + +/** + * @function connection_Release + * @abstract Releases a reference count, destroying on last release + * @discussion + * Only frees the memory on the final reference count. The pointer will + * always be NULL'd. + */ +void connection_Release(Connection **connectionPtr); + +/** + * @function connection_Acquire + * @abstract A reference counted copy. + * @discussion + * A shallow copy, they share the same memory. + */ +Connection *connection_Acquire(Connection *connection); + +/** + * @function connection_Send + * @abstract Sends the message on the connection + * @return true if message sent, false if connection not up + */ +bool connection_Send(const Connection *conn, Message *message); + +/** + * Return the `IoOperations` instance associated with the specified `Connection` + * instance. + * @param [in] connection The allocated connection + * @return a pointer to the IoOperations instance associated by th specified + * connection. + */ +IoOperations *connection_GetIoOperations(const Connection *conn); + +/** + * Returns the unique identifier of the connection + * Calls the underlying IoOperations to fetch the connection id + * @param [in] connection The allocated connection + * @return unsigned The unique connection id + */ +unsigned connection_GetConnectionId(const Connection *conn); + +/** + * Returns the (remote, local) address pair that describes the connection + * @param [in] connection The allocated connection + * @return non-null The connection's remote and local address + * @return null Should never return NULL + */ +const AddressPair *connection_GetAddressPair(const Connection *conn); + +/** + * Checks if the connection is in the "up" state + * @param [in] connection The allocated connection + * @return true The connection is in the "up" state + * @return false The connection is not in the "up" state + */ +bool connection_IsUp(const Connection *conn); + +/** + * Checks if the connection is to a Local/Loopback address + * + * A local connection is PF_LOCAL (PF_UNIX) and a loopback connection is + * 127.0.0.0/8 or ::1 for IPv6. + * + * @param [in] connection The allocated connection + * + * @retval true The connection is local or loopback + * @retval false The connection is not local or loopback + */ +bool connection_IsLocal(const Connection *conn); + +/** + * Returns an opaque pointer representing the class of the Io Operations + * + * Returns an opaque pointer that an implementation can use to detect if + * the connection is based on that class. + * + * @param [in] conn The Connection to analyze + * + * @return non-null An opaque pointer for each concrete implementation + */ +const void *connection_Class(const Connection *conn); + +bool connection_ReSend(const Connection *conn, Message *message, + bool notification); + +void connection_Probe(Connection *conn); + +void connection_HandleProbe(Connection *conn, uint8_t *message, + Ticks actualTime); + +uint64_t connection_GetDelay(Connection *conn); + +void connection_AllowWldrAutoStart(Connection *conn, bool allow); + +void connection_EnableWldr(Connection *conn); + +void connection_DisableWldr(Connection *conn); + +bool connection_HasWldr(const Connection *conn); + +bool connection_WldrAutoStartAllowed(const Connection *conn); + +void connection_DetectLosses(Connection *conn, Message *message); + +void connection_HandleWldrNotification(Connection *conn, Message *message); +#endif // connection_h diff --git a/hicn-light/src/core/connectionList.c b/hicn-light/src/core/connectionList.c new file mode 100755 index 000000000..b2913fa05 --- /dev/null +++ b/hicn-light/src/core/connectionList.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +#include +#include + +struct connection_list { + PARCArrayList *listOfConnections; +}; + +static void connectionList_ArrayDestroyer(void **voidPtr) { + Connection **entryPtr = (Connection **)voidPtr; + connection_Release(entryPtr); +} + +ConnectionList *connectionList_Create() { + ConnectionList *list = parcMemory_AllocateAndClear(sizeof(ConnectionList)); + parcAssertNotNull(list, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ConnectionList)); + list->listOfConnections = parcArrayList_Create(connectionList_ArrayDestroyer); + return list; +} + +void connectionList_Destroy(ConnectionList **listPtr) { + parcAssertNotNull(listPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*listPtr, "Parameter must dereference to non-null pointer"); + ConnectionList *list = *listPtr; + parcArrayList_Destroy(&list->listOfConnections); + parcMemory_Deallocate((void **)&list); + *listPtr = NULL; +} + +void connectionList_Append(ConnectionList *list, Connection *entry) { + parcAssertNotNull(list, "Parameter list must be non-null"); + parcAssertNotNull(entry, "Parameter entry must be non-null"); + + parcArrayList_Add(list->listOfConnections, connection_Acquire(entry)); +} + +size_t connectionList_Length(const ConnectionList *list) { + parcAssertNotNull(list, "Parameter list must be non-null"); + return parcArrayList_Size(list->listOfConnections); +} + +Connection *connectionList_Get(ConnectionList *list, size_t index) { + parcAssertNotNull(list, "Parameter list must be non-null"); + Connection *original = + (Connection *)parcArrayList_Get(list->listOfConnections, index); + return original; +} diff --git a/hicn-light/src/core/connectionList.h b/hicn-light/src/core/connectionList.h new file mode 100755 index 000000000..cdca12993 --- /dev/null +++ b/hicn-light/src/core/connectionList.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file connectionList.h + * @brief A typesafe list of Connection objects + * + * <#Detailed Description#> + * + */ + +#ifndef connectionList_h +#define connectionList_h + +struct connection_list; +typedef struct connection_list ConnectionList; + +#include + +/** + * Creates a lis of Connection + * + * @return non-null An allocated list + * @return null An error + */ +ConnectionList *connectionList_Create(void); + +/** + * Destroys the list and all objects inside it + */ +void connectionList_Destroy(ConnectionList **listPtr); + +/** + * @function connectionList_Append + * @abstract Adds a connection entry to the list. + * @discussion + * Acquires a reference to the passed entry and stores it in the list. + */ +void connectionList_Append(ConnectionList *list, Connection *entry); + +/** + * Returns the number of items on the list + * @param [in] list The allocated list to check + * @return number The number of items on the list + */ +size_t connectionList_Length(const ConnectionList *list); + +/** + * @function connectionList_Get + * @abstract Returns the connection entry. + * @discussion + * Caller must not destroy the returned value. If you will store the + * entry in your own data structure, you should acquire your own reference. + * Will assert if you go beyond the end of the list. + * + */ +Connection *connectionList_Get(ConnectionList *list, size_t index); +#endif // connectionList_h diff --git a/hicn-light/src/core/connectionManager.c b/hicn-light/src/core/connectionManager.c new file mode 100755 index 000000000..2089e1495 --- /dev/null +++ b/hicn-light/src/core/connectionManager.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * The Connection Manager sets itself up as a listener to the Messenger so it + * can take action based on system events. + * + * The Connection Manager queues and then processes in a later time slice the + * messages. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +struct connection_manager { + Forwarder *forwarder; + Logger *logger; + + MessengerRecipient *messengerRecipient; + + // we queue missives as they come in to process in our own + // event timeslice + MissiveDeque *missiveQueue; + + // for deferred queue processing + PARCEventTimer *timerEvent; +}; + +/** + * Receives missives from the messenger, queues them, and schedules our + * execution + * + * We defer processing of missives to a later time slice + */ +static void connectionManager_MessengerCallback(MessengerRecipient *recipient, + Missive *missive); + +/** + * Event callback + * + * This is our main run loop to process our queue of messages. It is scheduled + * in {@link connectionManager_MessengerCallback} when the queue becomes + * non-empty. + * + * When we are called here, we have exclusive use of the system, so we will not + * create any message loops + * + * @param [in] fd unused, required for compliance with function prototype + * @param [in] which_event unused, required for compliance with function + * prototype + * @param [in] connManagerVoidPtr A void* to ConnectionManager + * + */ +static void connectionManager_ProcessQueue(int fd, PARCEventType which_event, + void *connManagerVoidPtr); + +static void connectionManager_ProcessClosedMissive( + ConnectionManager *connManager, const Missive *missive); + +// ======================================================== +// Public API + +ConnectionManager *connectionManager_Create(Forwarder *forwarder) { + ConnectionManager *connManager = + parcMemory_AllocateAndClear(sizeof(ConnectionManager)); + parcAssertNotNull(connManager, + "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ConnectionManager)); + connManager->forwarder = forwarder; + connManager->missiveQueue = missiveDeque_Create(); + connManager->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + + Messenger *messenger = forwarder_GetMessenger(connManager->forwarder); + + // creates the timer, but does not start it + PARCEventScheduler *base = + dispatcher_GetEventScheduler(forwarder_GetDispatcher(forwarder)); + connManager->timerEvent = parcEventTimer_Create( + base, 0, connectionManager_ProcessQueue, connManager); + + connManager->messengerRecipient = messengerRecipient_Create( + connManager, connectionManager_MessengerCallback); + messenger_Register(messenger, connManager->messengerRecipient); + return connManager; +} + +void connectionManager_Destroy(ConnectionManager **managerPtr) { + parcAssertNotNull(managerPtr, "Double pointer must be non-null"); + parcAssertNotNull(*managerPtr, "Double pointer must dereference to non-null"); + + ConnectionManager *connManager = *managerPtr; + + Messenger *messenger = forwarder_GetMessenger(connManager->forwarder); + parcEventTimer_Destroy(&(connManager->timerEvent)); + messenger_Unregister(messenger, connManager->messengerRecipient); + messengerRecipient_Destroy(&connManager->messengerRecipient); + missiveDeque_Release(&connManager->missiveQueue); + logger_Release(&connManager->logger); + + parcMemory_Deallocate((void **)&connManager); + *managerPtr = NULL; +} + +// ======================================================== +// Internal Functions + +static void connectionManager_MessengerCallback(MessengerRecipient *recipient, + Missive *missive) { + ConnectionManager *connManager = + messengerRecipient_GetRecipientContext(recipient); + + // we do not release our reference count, we store it until later + // We are called with our own reference, so we do not need to acquire the + // missive here. + missiveDeque_Append(connManager->missiveQueue, missive); + + if (missiveDeque_Size(connManager->missiveQueue) == 1) { + // When it becomes non-empty, schedule {@link + // connectionManager_ProcessQueue} + struct timeval immediateTimeout = {0, 0}; + parcEventTimer_Start(connManager->timerEvent, &immediateTimeout); + } +} + +static void connectionManager_ProcessQueue(int fd, PARCEventType which_event, + void *connManagerVoidPtr) { + ConnectionManager *connManager = (ConnectionManager *)connManagerVoidPtr; + + Missive *missive; + while ((missive = missiveDeque_RemoveFirst(connManager->missiveQueue)) != + NULL) { + switch (missive_GetType(missive)) { + case MissiveType_ConnectionCreate: + // hook to signal that a new connection was created + break; + case MissiveType_ConnectionUp: + // hook to signal that a new connection is up + break; + case MissiveType_ConnectionDown: + // hook to signal that a connection is down + break; + case MissiveType_ConnectionClosed: + connectionManager_ProcessClosedMissive(connManager, missive); + break; + case MissiveType_ConnectionDestroyed: + // hook to signal that a connection was destroyed + break; + default: + parcTrapUnexpectedState("Missive %p of unknown type: %d", + (void *)missive, missive_GetType(missive)); + } + missive_Release(&missive); + } +} + +static void connectionManager_ProcessClosedMissive( + ConnectionManager *connManager, const Missive *missive) { + logger_Log(connManager->logger, LoggerFacility_Core, PARCLogLevel_Debug, + __func__, "Processing CLOSED message for connid %u", + missive_GetConnectionId(missive)); + + ConnectionTable *table = forwarder_GetConnectionTable(connManager->forwarder); + const Connection *conn = + connectionTable_FindById(table, missive_GetConnectionId(missive)); + + if (conn) { + // this will destroy the connection if its the last reference count + connectionTable_Remove(table, conn); + + // remove from FIB + forwarder_RemoveConnectionIdFromRoutes(connManager->forwarder, + missive_GetConnectionId(missive)); + } +} diff --git a/hicn-light/src/core/connectionManager.h b/hicn-light/src/core/connectionManager.h new file mode 100755 index 000000000..b77553e0d --- /dev/null +++ b/hicn-light/src/core/connectionManager.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file connectionManager.h + * @brief The connection manager handles connection events, such as going down + * + * The connection manager listens to the event notification system. Based on + * those events, the connection manager will take specific actions. This is + * expected to be a singleton instantiated by the forwarder. + * + */ + +#ifndef connectionManager_h +#define connectionManager_h + +#include + +struct connection_manager; +typedef struct connection_manager ConnectionManager; + +ConnectionManager *connectionManager_Create(Forwarder *forwarder); + +void connectionManager_Destroy(ConnectionManager **managerPtr); +#endif // connectionManager_h diff --git a/hicn-light/src/core/connectionTable.c b/hicn-light/src/core/connectionTable.c new file mode 100755 index 000000000..ba0942ddb --- /dev/null +++ b/hicn-light/src/core/connectionTable.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @header ConnectionTable + * @abstract Records all the current connections and references to them + * @discussion + * + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +struct connection_table { + // The main storage table that has a Destroy method. + // The key is an unsigned int pointer. We use an unsigned int pointer + // because we want to be able to lookup by the id alone, and not have to + // have the IoOperations everywhere. + PARCHashCodeTable *storageTableById; + + // The key is a AddressPair + // It does not have a destroy method for the data or key, + // as they are derived from the storage table. + PARCHashCodeTable *indexByAddressPair; + + // An iterable stucture organized by connection id. The keys and + // values are the same pointers as in storageTableById, so there + // are no destructors in the tree. + // The only reason to keep this tree is so we have an iterable list + // of connections, which the hash table does not give us. + PARCTreeRedBlack *listById; +}; + +static bool connectionTable_ConnectionIdEquals(const void *keyA, + const void *keyB) { + unsigned idA = *((unsigned *)keyA); + unsigned idB = *((unsigned *)keyB); + return (idA == idB); +} + +static int connectionTable_ConnectionIdCompare(const void *keyA, + const void *keyB) { + unsigned idA = *((unsigned *)keyA); + unsigned idB = *((unsigned *)keyB); + if (idA < idB) { + return -1; + } + if (idA > idB) { + return +1; + } + return 0; +} + +static bool connectionTable_AddressPairEquals(const void *keyA, + const void *keyB) { + const AddressPair *pairA = (const AddressPair *)keyA; + const AddressPair *pairB = (const AddressPair *)keyB; + + return addressPair_Equals(pairA, pairB); +} + +static HashCodeType connectionTable_ConnectionIdHashCode(const void *keyA) { + unsigned idA = *((unsigned *)keyA); + return parcHash32_Int32(idA); +} + +static HashCodeType connectionTable_AddressPairHashCode(const void *keyA) { + const AddressPair *pairA = (const AddressPair *)keyA; + return addressPair_HashCode(pairA); +} + +static void connectionTable_ConnectionIdDestroyer(void **dataPtr) { + unsigned *idA = (unsigned *)*dataPtr; + parcMemory_Deallocate((void **)&idA); + *dataPtr = NULL; +} + +static void connectionTable_ConnectionDestroyer(void **dataPtr) { + connection_Release((Connection **)dataPtr); +} + +ConnectionTable *connectionTable_Create() { + size_t initialSize = 16384; + + ConnectionTable *conntable = + parcMemory_AllocateAndClear(sizeof(ConnectionTable)); + parcAssertNotNull(conntable, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ConnectionTable)); + + conntable->storageTableById = parcHashCodeTable_Create_Size( + connectionTable_ConnectionIdEquals, connectionTable_ConnectionIdHashCode, + connectionTable_ConnectionIdDestroyer, + connectionTable_ConnectionDestroyer, initialSize); + + // no key or data destroyer, this is an index into storageByid. + conntable->indexByAddressPair = parcHashCodeTable_Create_Size( + connectionTable_AddressPairEquals, connectionTable_AddressPairHashCode, + NULL, NULL, initialSize); + + conntable->listById = + parcTreeRedBlack_Create(connectionTable_ConnectionIdCompare, + NULL, // key free + NULL, // key copy + NULL, // value equals + NULL, // value free + NULL); // value copy + + return conntable; +} + +void connectionTable_Destroy(ConnectionTable **conntablePtr) { + parcAssertNotNull(conntablePtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*conntablePtr, + "Parameter must dereference to non-null pointer"); + + ConnectionTable *conntable = *conntablePtr; + + parcTreeRedBlack_Destroy(&conntable->listById); + parcHashCodeTable_Destroy(&conntable->indexByAddressPair); + parcHashCodeTable_Destroy(&conntable->storageTableById); + parcMemory_Deallocate((void **)&conntable); + *conntablePtr = NULL; +} + +/** + * @function connectionTable_Add + * @abstract Add a connection, takes ownership of memory + */ +void connectionTable_Add(ConnectionTable *table, Connection *connection) { + parcAssertNotNull(table, "Parameter table must be non-null"); + parcAssertNotNull(connection, "Parameter connection must be non-null"); + + unsigned *connectionIdKey = parcMemory_Allocate(sizeof(unsigned)); + parcAssertNotNull(connectionIdKey, "parcMemory_Allocate(%zu) returned NULL", + sizeof(unsigned)); + *connectionIdKey = connection_GetConnectionId(connection); + + if (parcHashCodeTable_Add(table->storageTableById, connectionIdKey, + connection)) { + parcHashCodeTable_Add(table->indexByAddressPair, + (void *)connection_GetAddressPair(connection), + connection); + parcTreeRedBlack_Insert(table->listById, connectionIdKey, connection); + } else { + parcTrapUnexpectedState( + "Could not add connection id %u -- is it a duplicate?", + *connectionIdKey); + } +} + +/** + * @function connectionTable_Remove + * @abstract Removes the connection, calling Destroy on our copy + */ +void connectionTable_Remove(ConnectionTable *table, + const Connection *connection) { + parcAssertNotNull(table, "Parameter table must be non-null"); + parcAssertNotNull(connection, "Parameter connection must be non-null"); + + unsigned connid = connection_GetConnectionId(connection); + + parcTreeRedBlack_Remove(table->listById, &connid); + parcHashCodeTable_Del(table->indexByAddressPair, + connection_GetAddressPair(connection)); + parcHashCodeTable_Del(table->storageTableById, &connid); +} + +void connectionTable_RemoveById(ConnectionTable *table, unsigned id) { + parcAssertNotNull(table, "Parameter table must be non-null"); + const Connection *connection = connectionTable_FindById(table, id); + if (connection) { + connectionTable_Remove(table, connection); + } +} + +const Connection *connectionTable_FindByAddressPair(ConnectionTable *table, + const AddressPair *pair) { + parcAssertNotNull(table, "Parameter table must be non-null"); + return (Connection *)parcHashCodeTable_Get(table->indexByAddressPair, pair); +} + +const Connection *connectionTable_FindById(ConnectionTable *table, + unsigned id) { + parcAssertNotNull(table, "Parameter table must be non-null"); + return (Connection *)parcHashCodeTable_Get(table->storageTableById, &id); +} + +ConnectionList *connectionTable_GetEntries(const ConnectionTable *table) { + parcAssertNotNull(table, "Parameter table must be non-null"); + ConnectionList *list = connectionList_Create(); + + PARCArrayList *values = parcTreeRedBlack_Values(table->listById); + for (size_t i = 0; i < parcArrayList_Size(values); i++) { + Connection *original = parcArrayList_Get(values, i); + connectionList_Append(list, original); + } + parcArrayList_Destroy(&values); + return list; +} diff --git a/hicn-light/src/core/connectionTable.h b/hicn-light/src/core/connectionTable.h new file mode 100755 index 000000000..30517ae1d --- /dev/null +++ b/hicn-light/src/core/connectionTable.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#ifndef connectionTable_h +#define connectionTable_h + +#include +#include +#include +#include + +struct connection_table; +typedef struct connection_table ConnectionTable; + +/** + * Creates an empty connection table + */ +ConnectionTable *connectionTable_Create(void); + +/** + * Destroys the connection table + * This will release the reference to all connections stored in the connection + * table. + * @param [in,out] conntablePtr Pointer to the allocated connection table, will + * be NULL'd + */ +void connectionTable_Destroy(ConnectionTable **conntablePtr); + +/** + * @function connectionTable_Add + * @abstract Add a connection, takes ownership of memory + */ +void connectionTable_Add(ConnectionTable *table, Connection *connection); + +/** + * @function connectionTable_Remove + * @abstract Removes the connection, calling Destroy on our copy + */ +void connectionTable_Remove(ConnectionTable *table, + const Connection *connection); + +/** + * Removes a connection from the connection table + * + * Looks up a connection by its connection ID and removes it from the connection + * table. Removing the connection will call connection_Release() on the + * connection object. + * + * @param [in] table The allocated connection table + * @param [in] id The connection ID + */ +void connectionTable_RemoveById(ConnectionTable *table, unsigned id); + +/** + * Lookup a connection by the (local, remote) addres pair + * + * @param [in] table The allocated connection table + * @param [in] pair The address pair to match, based on the inner values of the + * local and remote addresses + * + * @retval non-null The matched conneciton + * @retval null No match found or error + */ +const Connection *connectionTable_FindByAddressPair(ConnectionTable *table, + const AddressPair *pair); + +/** + * @function connectionTable_FindById + * @abstract Find a connection by its numeric id. + * @return NULL if not found + */ +const Connection *connectionTable_FindById(ConnectionTable *table, unsigned id); + +/** + * @function connectionTable_GetEntries + * @abstract Returns a list of connections. They are reference counted copies + * from the table. + * @discussion + * An allocated list of connections in the table. Each list entry is a + * reference counted copy of the connection in the table, thus they are "live" + * objects. + */ +ConnectionList *connectionTable_GetEntries(const ConnectionTable *table); +#endif // connectionTable_h diff --git a/hicn-light/src/core/dispatcher.c b/hicn-light/src/core/dispatcher.c new file mode 100755 index 000000000..078087c59 --- /dev/null +++ b/hicn-light/src/core/dispatcher.c @@ -0,0 +1,435 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @header dispatcher.c + * @abstract Event dispatcher for hicn-light. Uses parcEvent + * @discussion + * Wraps the functions we use in parcEvent, along with StreamBuffer and + * Message. The dispatcher is the event loop, so it manages things like signals, + * timers, and network events. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include + +#ifndef INPORT_ANY +#define INPORT_ANY 0 +#endif + +struct dispatcher { + PARCEventScheduler *Base; + Logger *logger; +}; + +// ========================================== +// Public API + +PARCEventScheduler *dispatcher_GetEventScheduler(Dispatcher *dispatcher) { + return dispatcher->Base; +} + +Dispatcher *dispatcher_Create(Logger *logger) { + Dispatcher *dispatcher = parcMemory_AllocateAndClear(sizeof(Dispatcher)); + parcAssertNotNull(dispatcher, + "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Dispatcher)); + + dispatcher->Base = parcEventScheduler_Create(); + dispatcher->logger = logger_Acquire(logger); + + parcAssertNotNull(dispatcher->Base, + "Got NULL from parcEventScheduler_Create()"); + + return dispatcher; +} + +void dispatcher_Destroy(Dispatcher **dispatcherPtr) { + parcAssertNotNull(dispatcherPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*dispatcherPtr, + "Parameter must dereference to non-null pointer"); + Dispatcher *dispatcher = *dispatcherPtr; + + logger_Release(&dispatcher->logger); + parcEventScheduler_Destroy(&(dispatcher->Base)); + parcMemory_Deallocate((void **)&dispatcher); + *dispatcherPtr = NULL; +} + +void dispatcher_Stop(Dispatcher *dispatcher) { + struct timeval delay = {0, 1000}; + + parcEventScheduler_Stop(dispatcher->Base, &delay); +} + +void dispatcher_Run(Dispatcher *dispatcher) { + parcAssertNotNull(dispatcher, "Parameter must be non-null"); + + parcEventScheduler_Start(dispatcher->Base, 0); +} + +void dispatcher_RunDuration(Dispatcher *dispatcher, struct timeval *duration) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(duration, "Parameter duration must be non-null"); + + parcEventScheduler_Stop(dispatcher->Base, duration); + parcEventScheduler_Start(dispatcher->Base, 0); +} + +void dispatcher_RunCount(Dispatcher *dispatcher, unsigned count) { + parcAssertNotNull(dispatcher, "Parameter must be non-null"); + + for (unsigned i = 0; i < count; i++) { + parcEventScheduler_Start(dispatcher->Base, + PARCEventSchedulerDispatchType_LoopOnce); + } +} + +PARCEventSocket *dispatcher_CreateListener(Dispatcher *dispatcher, + PARCEventSocket_Callback *callback, + void *user_data, int backlog, + const struct sockaddr *sa, + int socklen) { + PARCEventSocket *listener = parcEventSocket_Create( + dispatcher->Base, callback, NULL, user_data, sa, socklen); + if (listener == NULL) { + perror("Problem creating listener"); + } + return listener; +} + +void dispatcher_DestroyListener(Dispatcher *dispatcher, + PARCEventSocket **listenerPtr) { + parcAssertNotNull(listenerPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*listenerPtr, + "Parameter must dereference to non-null pointer"); + parcEventSocket_Destroy(listenerPtr); +} + +PARCEventQueue *dispatcher_CreateStreamBufferFromSocket(Dispatcher *dispatcher, + SocketType fd) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + PARCEventQueue *buffer = parcEventQueue_Create( + dispatcher->Base, fd, + PARCEventQueueOption_CloseOnFree | PARCEventQueueOption_DeferCallbacks); + parcAssertNotNull(buffer, + "Got null from parcEventBufver_Create for socket %d", fd); + return buffer; +} + +PARCEventTimer *dispatcher_CreateTimer(Dispatcher *dispatcher, bool isPeriodic, + PARCEvent_Callback *callback, + void *userData) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(callback, "Parameter callback must be non-null"); + + PARCEventType flags = 0; + if (isPeriodic) { + flags |= PARCEventType_Persist; + } + PARCEventTimer *event = + parcEventTimer_Create(dispatcher->Base, flags, callback, userData); + return event; +} + +void dispatcher_StartTimer(Dispatcher *dispatcher, PARCEventTimer *timerEvent, + struct timeval *delay) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(timerEvent, "Parameter timerEvent must be non-null"); + int failure = parcEventTimer_Start(timerEvent, delay); + parcAssertFalse(failure < 0, "Error starting timer event %p: (%d) %s", + (void *)timerEvent, errno, strerror(errno)); +} + +void dispatcher_StopTimer(Dispatcher *dispatcher, PARCEventTimer *event) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(event, "Parameter event must be non-null"); + + int failure = parcEventTimer_Stop(event); + parcAssertFalse(failure < 0, "Error stopping signal event %p: (%d) %s", + (void *)event, errno, strerror(errno)); +} + +void dispatcher_DestroyTimerEvent(Dispatcher *dispatcher, + PARCEventTimer **eventPtr) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(eventPtr, + "Parameter eventPtr must be non-null double pointer"); + parcAssertNotNull(*eventPtr, + "Paramter eventPtr must dereference to non-null pointer"); + + parcEventTimer_Destroy(eventPtr); + eventPtr = NULL; +} + +PARCEvent *dispatcher_CreateNetworkEvent(Dispatcher *dispatcher, + bool isPersistent, + PARCEvent_Callback *callback, + void *userData, int fd) { + short flags = PARCEventType_Timeout | PARCEventType_Read; + if (isPersistent) { + flags |= PARCEventType_Persist; + } + + PARCEvent *event = + parcEvent_Create(dispatcher->Base, fd, flags, callback, userData); + parcAssertNotNull(event, "Got null from parcEvent_Create for socket %d", fd); + return event; +} + +void dispatcher_DestroyNetworkEvent(Dispatcher *dispatcher, + PARCEvent **eventPtr) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(eventPtr, + "Parameter eventPtr must be non-null double pointer"); + parcAssertNotNull(*eventPtr, + "Paramter eventPtr must dereference to non-null pointer"); + + parcEvent_Destroy(eventPtr); + eventPtr = NULL; +} + +void dispatcher_StartNetworkEvent(Dispatcher *dispatcher, PARCEvent *event) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(event, "Parameter event must be non-null"); + + int failure = parcEvent_Start(event); + parcAssertFalse(failure < 0, "Error starting signal event %p: (%d) %s", + (void *)event, errno, strerror(errno)); +} + +void dispatcher_StopNetworkEvent(Dispatcher *dispatcher, PARCEvent *event) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(event, "Parameter event must be non-null"); + + int failure = parcEvent_Stop(event); + parcAssertFalse(failure < 0, "Error stopping signal event %p: (%d) %s", + (void *)event, errno, strerror(errno)); +} + +PARCEventSignal *dispatcher_CreateSignalEvent( + Dispatcher *dispatcher, PARCEventSignal_Callback *callback, void *userData, + int signal) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(callback, "Parameter callback must be non-null"); + + PARCEventSignal *event = parcEventSignal_Create( + dispatcher->Base, signal, PARCEventType_Signal | PARCEventType_Persist, + callback, userData); + parcAssertNotNull(event, + "Got null event when creating signal catcher for signal %d", + signal); + + return event; +} + +void dispatcher_DestroySignalEvent(Dispatcher *dispatcher, + PARCEventSignal **eventPtr) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(eventPtr, + "Parameter eventPtr must be non-null double pointer"); + parcAssertNotNull(*eventPtr, + "Paramter eventPtr must dereference to non-null pointer"); + + parcEventSignal_Destroy(eventPtr); + eventPtr = NULL; +} + +void dispatcher_StartSignalEvent(Dispatcher *dispatcher, + PARCEventSignal *event) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(event, "Parameter event must be non-null"); + + int failure = parcEventSignal_Start(event); + parcAssertFalse(failure < 0, "Error starting signal event %p: (%d) %s", + (void *)event, errno, strerror(errno)); +} + +void dispatcher_StopSignalEvent(Dispatcher *dispatcher, + PARCEventSignal *event) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(event, "Parameter event must be non-null"); + + int failure = parcEventSignal_Stop(event); + parcAssertFalse(failure < 0, "Error stopping signal event %p: (%d) %s", + (void *)event, errno, strerror(errno)); +} + +/** + * Bind to a local address/port then connect to peer. + */ +static bool dispatcher_StreamBufferBindAndConnect(Dispatcher *dispatcher, + PARCEventQueue *buffer, + struct sockaddr *localSock, + socklen_t localSockLength, + struct sockaddr *remoteSock, + socklen_t remoteSockLength) { + // we need to bind, then connect. Special operation, so we make our + // own fd then pass it off to the buffer event + + int fd = socket(localSock->sa_family, SOCK_STREAM, 0); + if (fd < 0) { + perror("socket"); + return -1; + } + + // Set non-blocking flag + int flags = fcntl(fd, F_GETFL, NULL); + if (flags < 0) { + perror("F_GETFL"); + close(fd); + return -1; + } + int failure = fcntl(fd, F_SETFL, flags | O_NONBLOCK); + if (failure) { + perror("F_SETFL"); + close(fd); + return -1; + } + + failure = bind(fd, localSock, localSockLength); + if (failure) { + perror("bind"); + close(fd); + return false; + } + + parcEventQueue_SetFileDescriptor(buffer, fd); + + failure = parcEventQueue_ConnectSocket(buffer, remoteSock, remoteSockLength); + if (failure && (errno != EINPROGRESS)) { + perror("connect"); + close(fd); + return false; + } + return true; +} + +/** + * Connect to an INET peer + * @return NULL on error, otherwise a streambuffer + */ +static PARCEventQueue *dispatcher_StreamBufferConnect_INET( + Dispatcher *dispatcher, const Address *localAddress, + const Address *remoteAddress) { + struct sockaddr_in localSock, remoteSock; + addressGetInet(localAddress, &localSock); + addressGetInet(remoteAddress, &remoteSock); + + PARCEventQueue *buffer = parcEventQueue_Create( + dispatcher->Base, -1, PARCEventQueueOption_CloseOnFree); + parcAssertNotNull(buffer, "got null buffer from parcEventQueue_Create()"); + + bool success = dispatcher_StreamBufferBindAndConnect( + dispatcher, buffer, (struct sockaddr *)&localSock, sizeof(localSock), + (struct sockaddr *)&remoteSock, sizeof(remoteSock)); + if (!success) { + parcEventQueue_Destroy(&buffer); + buffer = NULL; + } + + return buffer; +} + +/** + * Connect to an INET peer + * @return NULL on error, otherwise a streambuffer + */ +static PARCEventQueue * +// static StreamBuffer * +dispatcher_StreamBufferConnect_INET6(Dispatcher *dispatcher, + const Address *localAddress, + const Address *remoteAddress) { + struct sockaddr_in6 localSock, remoteSock; + addressGetInet6(localAddress, &localSock); + addressGetInet6(remoteAddress, &remoteSock); + + PARCEventQueue *buffer = parcEventQueue_Create( + dispatcher->Base, -1, PARCEventQueueOption_CloseOnFree); + parcAssertNotNull(buffer, "got null buffer from parcEventQueue_Create()"); + + bool success = dispatcher_StreamBufferBindAndConnect( + dispatcher, buffer, (struct sockaddr *)&localSock, sizeof(localSock), + (struct sockaddr *)&remoteSock, sizeof(remoteSock)); + if (!success) { + parcEventQueue_Destroy(&buffer); + buffer = NULL; + } + + return buffer; +} + +PARCEventQueue *dispatcher_StreamBufferConnect(Dispatcher *dispatcher, + const AddressPair *pair) { + const Address *localAddress = addressPair_GetLocal(pair); + const Address *remoteAddress = addressPair_GetRemote(pair); + + // they must be of the same address family + if (addressGetType(localAddress) != addressGetType(remoteAddress)) { + char message[2048]; + char *localAddressString = addressToString(localAddress); + char *remoteAddressString = addressToString(remoteAddress); + snprintf(message, 2048, + "Remote address not same type as local address, expected %d got " + "%d\nlocal %s remote %s", + addressGetType(localAddress), addressGetType(remoteAddress), + localAddressString, remoteAddressString); + + parcMemory_Deallocate((void **)&localAddressString); + parcMemory_Deallocate((void **)&remoteAddressString); + + parcAssertTrue( + addressGetType(localAddress) == addressGetType(remoteAddress), "%s", + message); + } + + address_type type = addressGetType(localAddress); + + PARCEventQueue *result = NULL; + + switch (type) { + case ADDR_INET: + return dispatcher_StreamBufferConnect_INET(dispatcher, localAddress, + remoteAddress); + break; + case ADDR_INET6: + return dispatcher_StreamBufferConnect_INET6(dispatcher, localAddress, + remoteAddress); + break; + default: + parcTrapIllegalValue(type, "local address unsupported address type: %d", + type); + } + return result; +} diff --git a/hicn-light/src/core/dispatcher.h b/hicn-light/src/core/dispatcher.h new file mode 100755 index 000000000..35d804a00 --- /dev/null +++ b/hicn-light/src/core/dispatcher.h @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @header hicn-light Dispatcher + * @abstract The dispatcher is the event loop run by Forwarder. + * @discussion + * These functions manage listeners, timers, and network events inside + * the event loop. + * + * Curently, it is a thin wrapper around an event so we don't have to + * expose that implementation detail to other modules. + * + */ + +#ifndef dispatcher_h +#define dispatcher_h + +#include +#include + +struct dispatcher; +typedef struct dispatcher Dispatcher; + +#include +#include +#include +#include +#include +#include +#include + +#include + +PARCEventScheduler *dispatcher_GetEventScheduler(Dispatcher *dispatcher); +/** + * Creates an event dispatcher + * + * Event dispatcher based on PARCEvent + * + * @return non-null Allocated event dispatcher + * @return null An error + */ +Dispatcher *dispatcher_Create(Logger *logger); + +/** + * Destroys event dispatcher + * + * Caller is responsible for destroying call events before destroying + * the event dispatcher. + */ +void dispatcher_Destroy(Dispatcher **dispatcherPtr); + +/** + * @function dispatcher_Stop + * @abstract Called from a different thread, tells the dispatcher to stop + * @discussion + * Called from a user thread or from an interrupt handler. + * Does not block. Use dispatcher_WaitForStopped() to + * block until stopped after calling this. + */ +void dispatcher_Stop(Dispatcher *dispatcher); + +/** + * @function dispatcher_WaitForStopped + * @abstract Blocks until dispatcher in stopped state + * @discussion + * Used after dispatcher_Stop() to wait for stop. + */ +void dispatcher_WaitForStopped(Dispatcher *dispatcher); + +/** + * @function dispatcher_Run + * @abstract Runs the forwarder, blocks. + */ +void dispatcher_Run(Dispatcher *dispatcher); + +/** + * @function dispatcher_RunDuration + * @abstract Runs forwarder for at most duration, blocks. + * @discussion + * Blocks running the forwarder for a duration. May be called + * iteratively to keep running. Duration is a minimum, actual + * runtime may be slightly longer. + */ +void dispatcher_RunDuration(Dispatcher *dispatcher, struct timeval *duration); + +/** + * @header dispatcher_RunCount + * @abstract Run the event loop for the given count cycles + * @discussion + * Runs the event loop for the given number of cycles, blocking + * until done. May be called sequentially over and over. + * + */ +void dispatcher_RunCount(Dispatcher *dispatcher, unsigned count); + +typedef int SocketType; + +typedef struct evconnlistener Listener; + +/** + * @typedef ListenerCallback + * @abstract Callback function typedef for a stream listener + * + * @constant listener is the object created by forwarder_NewBind() + * that received the client connection + * @constant client_socket is the client socket + * @constant user_data is the user_data passed to + * forwarder_NewBind() + * @constant client_addr is the client address + * @constant socklen is the length of client_addr + * @discussion <#Discussion#> + */ +typedef void(ListenerCallback)(Listener *listener, SocketType client_socket, + struct sockaddr *client_addr, int socklen, + void *user_data); + +/** + * @header forwarder_NewBind + * @abstract Allocate a new stream listener + * @discussion + * The server socket will be freed when closed and will be reusable. + * + * @param forwarder that owns the event loop + * @param cb is the callback for a new connection + * @param user_data is opaque user data passed to the callback + * @param backlog is the listen() depth, may use -1 for a default value + * @param sa is the socket address to bind to (INET, INET6, LOCAL) + * @param socklen is the sizeof the actual sockaddr (e.g. sizeof(sockaddr_un)) + */ +PARCEventSocket *dispatcher_CreateListener(Dispatcher *dispatcher, + PARCEventSocket_Callback *callback, + void *user_data, int backlog, + const struct sockaddr *sa, + int socklen); + +void dispatcher_DestroyListener(Dispatcher *dispatcher, + PARCEventSocket **listenerPtr); + +typedef struct event TimerEvent; +typedef struct event NetworkEvent; +typedef struct event SignalEvent; + +/** + * @typedef EventCallback + * @abstract A network event or a timer callback + * @constant fd The file descriptor associated with the event, may be -1 for + * timers + * @constant which_event is a bitmap of the EventType + * @constant user_data is the user_data passed to + * Forwarder_CreateEvent() + */ +typedef void(EventCallback)(SocketType fd, short which_event, void *user_data); + +/** + * @function dispatcher_CreateTimer + * @abstract Creates a Event for use as a timer. + * @discussion + * + * When created, the timer is idle and you need to call + * forwarder_StartTimer() + * + * @param isPeriodic means the timer will fire repeatidly, otherwise it is a + * one-shot and needs to be set again with dispatcher_StartTimer() + */ +PARCEventTimer *dispatcher_CreateTimer(Dispatcher *dispatcher, bool isPeriodic, + PARCEvent_Callback *callback, + void *userData); + +/** + * @function dispatcher_StartTimer + * @abstract Starts the timer with the given delay. + * @discussion + * If the timer is periodic, it will keep firing with the given delay + */ +void dispatcher_StartTimer(Dispatcher *dispatcher, PARCEventTimer *timerEvent, + struct timeval *delay); + +void dispatcher_StopTimer(Dispatcher *dispatcher, PARCEventTimer *timerEvent); + +/** + * @function dispatcher_DestroyTimerEvent + * @abstract Cancels the timer and frees the event + */ +void dispatcher_DestroyTimerEvent(Dispatcher *dispatcher, + PARCEventTimer **eventPtr); + +/** + * @function dispatcher_CreateNetworkEvent + * @abstract Creates a network event callback on the socket + * @discussion + * May be used on any sort of file descriptor or socket. The event is edge + * triggered and non-reentrent. This means you need to drain the events off the + * socket, as the callback will not be called again until a new event arrives. + * + * When created, the event is idle and you need to call + * forwarder_StartNetworkEvent() + * + * @param isPersistent means the callback will keep firing with new events, + * otherwise its a one-shot + * @param fd is the socket to monitor + */ +PARCEvent *dispatcher_CreateNetworkEvent(Dispatcher *dispatcher, + bool isPersistent, + PARCEvent_Callback *callback, + void *userData, int fd); + +void dispatcher_StartNetworkEvent(Dispatcher *dispatcher, PARCEvent *event); +void dispatcher_StopNetworkEvent(Dispatcher *dispatcher, PARCEvent *event); + +void dispatcher_DestroyNetworkEvent(Dispatcher *dispatcher, + PARCEvent **eventPtr); + +/** + * @function dispatcher_CreateSignalEvent + * @abstract Creates a signal trap + * @discussion + * May be used on catchable signals. The event is edge triggered and + * non-reentrent. Signal events are persistent. + * + * When created, the signal trap is idle and you need to call + * forwarder_StartSignalEvent() + * + * @param signal is the system signal to monitor (e.g. SIGINT). + * @return <#return#> + */ +PARCEventSignal *dispatcher_CreateSignalEvent( + Dispatcher *dispatcher, PARCEventSignal_Callback *callback, void *userData, + int signal); + +void dispatcher_DestroySignalEvent(Dispatcher *dispatcher, + PARCEventSignal **eventPtr); + +void dispatcher_StartSignalEvent(Dispatcher *dispatcher, + PARCEventSignal *event); +void dispatcher_StopSignalEvent(Dispatcher *dispatcher, PARCEventSignal *event); + +// ============= +// stream buffers + +#include +#include + +/** + * @function dispatcher_CreateStreamBuffer + * @abstract Creates a high-function buffer around a stream socket + */ +PARCEventQueue *dispatcher_CreateStreamBufferFromSocket(Dispatcher *dispatcher, + SocketType fd); + +/** + * @function dispatcher_StreamBufferConnect + * @abstract Create a TCP tunnel to a remote peer + * @discussion + * For TCP, both address pairs need to be the same address family: both INET + * or both INET6. The remote address must have the complete socket information + * (address, port). The local socket could be wildcarded or may specify down to + * the (address, port) pair. + * + * If the local address is IPADDR_ANY and the port is 0, then it is a normal + * call to "connect" that will use whatever local IP address and whatever local + * port for the connection. If either the address or port is set, the local + * socket will first be bound (via bind(2)), and then call connect(). + * + * It is unlikely that the buffer will be connected by the time the function + * returns. The eventCallback will fire once the remote system accepts the + * conneciton. + * + * @return NULL on error, otherwise a streambuffer. + */ +PARCEventQueue *dispatcher_StreamBufferConnect(Dispatcher *dispatcher, + const AddressPair *pair); +#endif // dispatcher_h diff --git a/hicn-light/src/core/forwarder.c b/hicn-light/src/core/forwarder.c new file mode 100755 index 000000000..cb94af3b5 --- /dev/null +++ b/hicn-light/src/core/forwarder.c @@ -0,0 +1,499 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Event based router + * + * This module is the glue around the event scheduler. + * Its the packet i/o module. + * + * Packet processing is done in dispatcher.c, which is the actual wrapper around + * the event scheduler + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define __STDC_FORMAT_MACROS +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#ifdef WITH_MAPME +#include +#endif /* WITH_MAPME */ +#include +#include +#include +#include + +#include + +#include + +// the router's clock frequency (we now use the monotonic clock) +#define HZ 1000 + +// these will all be a little off because its all integer division +#define MSEC_PER_TICK (1000 / HZ) +#define USEC_PER_TICK (1000000 / HZ) +#define NSEC_PER_TICK ((1000000000ULL) / HZ) +#define MSEC_TO_TICKS(msec) \ + ((msec < FC_MSEC_PER_TICK) ? 1 : msec / FC_MSEC_PER_TICK) +#define NSEC_TO_TICKS(nsec) ((nsec < NSEC_PER_TICK) ? 1 : nsec / NSEC_PER_TICK) + +struct forwarder { + Dispatcher *dispatcher; + + uint16_t server_port; + + PARCEventSignal *signal_int; + PARCEventSignal *signal_term; + PARCEventSignal *signal_usr1; + PARCEventTimer *keepalive_event; + + // will skew the virtual clock forward. In normal operaiton, it is 0. + Ticks clockOffset; + + unsigned nextConnectionid; + Messenger *messenger; + ConnectionManager *connectionManager; + ConnectionTable *connectionTable; + ListenerSet *listenerSet; + Configuration *config; + + // we'll eventually want to setup a threadpool of these + MessageProcessor *processor; + + Logger *logger; + + PARCClock *clock; + + hicn_socket_helper_t + *hicnSocketHelper; // state required to manage hicn connections + + // used by seed48 and nrand48 + unsigned short seed[3]; + +#ifdef WITH_MAPME + MapMe *mapme; +#endif /* WITH_MAPME */ +}; + +// signal traps through the event scheduler +static void _signal_cb(int, PARCEventType, void *); + +// A no-op keepalive to prevent Libevent from exiting the dispatch loop +static void _keepalive_cb(int, PARCEventType, void *); + +/** + * Reseed our pseudo-random number generator. + */ +static void forwarder_Seed(Forwarder *forwarder) { + int fd; + ssize_t res; + + res = -1; + fd = open("/dev/urandom", O_RDONLY); + if (fd != -1) { + res = read(fd, forwarder->seed, sizeof(forwarder->seed)); + close(fd); + } + if (res != sizeof(forwarder->seed)) { + forwarder->seed[1] = (unsigned short)getpid(); /* better than no entropy */ + forwarder->seed[2] = (unsigned short)time(NULL); + } + /* + * The call to seed48 is needed by cygwin, and should be harmless + * on other platforms. + */ + seed48(forwarder->seed); +} + +Logger *forwarder_GetLogger(const Forwarder *forwarder) { + return forwarder->logger; +} + +// ============================================================================ +// Setup and destroy section + +Forwarder *forwarder_Create(Logger *logger) { + Forwarder *forwarder = parcMemory_AllocateAndClear(sizeof(Forwarder)); + parcAssertNotNull(forwarder, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Forwarder)); + memset(forwarder, 0, sizeof(Forwarder)); + forwarder_Seed(forwarder); + + forwarder->clock = parcClock_Monotonic(); + forwarder->clockOffset = 0; + + if (logger) { + forwarder->logger = logger_Acquire(logger); + logger_SetClock(forwarder->logger, forwarder->clock); + } else { + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + forwarder->logger = logger_Create(reporter, forwarder->clock); + parcLogReporter_Release(&reporter); + } + + forwarder->nextConnectionid = 1; + forwarder->dispatcher = dispatcher_Create(forwarder->logger); + forwarder->messenger = messenger_Create(forwarder->dispatcher); + forwarder->connectionManager = connectionManager_Create(forwarder); + forwarder->connectionTable = connectionTable_Create(); + forwarder->listenerSet = listenerSet_Create(); + forwarder->config = configuration_Create(forwarder); + forwarder->processor = messageProcessor_Create(forwarder); + + forwarder->signal_term = dispatcher_CreateSignalEvent( + forwarder->dispatcher, _signal_cb, forwarder, SIGTERM); + dispatcher_StartSignalEvent(forwarder->dispatcher, forwarder->signal_term); + + forwarder->signal_int = dispatcher_CreateSignalEvent( + forwarder->dispatcher, _signal_cb, forwarder, SIGINT); + dispatcher_StartSignalEvent(forwarder->dispatcher, forwarder->signal_int); + + forwarder->signal_usr1 = dispatcher_CreateSignalEvent( + forwarder->dispatcher, _signal_cb, forwarder, SIGPIPE); + dispatcher_StartSignalEvent(forwarder->dispatcher, forwarder->signal_usr1); + +#ifndef __APPLE__ + forwarder->hicnSocketHelper = hicn_create(); + if (forwarder->hicnSocketHelper == NULL) return NULL; +#endif /* __APPLE__ */ + /* ignore child */ + signal(SIGCHLD, SIG_IGN); + + /* ignore tty signals */ + signal(SIGTSTP, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + signal(SIGTTIN, SIG_IGN); + + // We no longer use this for ticks, but we need to have at least one event + // schedule to keep Libevent happy. + + struct timeval wtnow_timeout; + timerclear(&wtnow_timeout); + + wtnow_timeout.tv_sec = 0; + wtnow_timeout.tv_usec = 50000; // 20 Hz keepalive + +#ifdef WITH_MAPME + if (!(mapMe_Init(&forwarder->mapme, forwarder))) return NULL; +#endif /* WITH_MAPME */ + + PARCEventScheduler *base = + dispatcher_GetEventScheduler(forwarder->dispatcher); + forwarder->keepalive_event = parcEventTimer_Create( + base, PARCEventType_Persist, _keepalive_cb, (void *)forwarder); + parcEventTimer_Start(forwarder->keepalive_event, &wtnow_timeout); + + return forwarder; +} + +void forwarder_Destroy(Forwarder **ptr) { + parcAssertNotNull(ptr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*ptr, "Parameter must dereference to non-null pointer"); + Forwarder *forwarder = *ptr; +#if !defined(__APPLE__) && !defined(__ANDROID__) + hicn_destroy(); +#endif + parcEventTimer_Destroy(&(forwarder->keepalive_event)); + + listenerSet_Destroy(&(forwarder->listenerSet)); + connectionManager_Destroy(&(forwarder->connectionManager)); + connectionTable_Destroy(&(forwarder->connectionTable)); + messageProcessor_Destroy(&(forwarder->processor)); + configuration_Destroy(&(forwarder->config)); + + // the messenger is used by many of the other pieces, so destroy it last + messenger_Destroy(&(forwarder->messenger)); + + dispatcher_DestroySignalEvent(forwarder->dispatcher, + &(forwarder->signal_int)); + dispatcher_DestroySignalEvent(forwarder->dispatcher, + &(forwarder->signal_term)); + dispatcher_DestroySignalEvent(forwarder->dispatcher, + &(forwarder->signal_usr1)); + + parcClock_Release(&forwarder->clock); + logger_Release(&forwarder->logger); + + // do the dispatcher last + dispatcher_Destroy(&(forwarder->dispatcher)); + + parcMemory_Deallocate((void **)&forwarder); + *ptr = NULL; +} + +void forwarder_SetupAllListeners(Forwarder *forwarder, uint16_t port, + const char *localPath) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + + configurationListeners_SetupAll(forwarder->config, port, localPath); +} + +void forwarder_SetupFromConfigFile(Forwarder *forwarder, const char *filename) { + ConfigurationFile *configFile = configurationFile_Create(forwarder, filename); + if (configFile) { + configurationFile_Process(configFile); + configurationFile_Release(&configFile); + } +} + +Configuration *forwarder_GetConfiguration(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return forwarder->config; +} + +// ============================================================================ + +unsigned forwarder_GetNextConnectionId(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return forwarder->nextConnectionid++; +} + +Messenger *forwarder_GetMessenger(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return forwarder->messenger; +} + +Dispatcher *forwarder_GetDispatcher(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return forwarder->dispatcher; +} + +ConnectionTable *forwarder_GetConnectionTable(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return forwarder->connectionTable; +} + +ListenerSet *forwarder_GetListenerSet(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return forwarder->listenerSet; +} + +void forwarder_SetChacheStoreFlag(Forwarder *forwarder, bool val) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + messageProcessor_SetCacheStoreFlag(forwarder->processor, val); +} + +bool forwarder_GetChacheStoreFlag(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return messageProcessor_GetCacheStoreFlag(forwarder->processor); +} + +void forwarder_SetChacheServeFlag(Forwarder *forwarder, bool val) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + messageProcessor_SetCacheServeFlag(forwarder->processor, val); +} + +bool forwarder_GetChacheServeFlag(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return messageProcessor_GetCacheServeFlag(forwarder->processor); +} + +void forwarder_ReceiveCommand(Forwarder *forwarder, command_id command, + struct iovec *message, unsigned ingressId) { + configuration_ReceiveCommand(forwarder->config, command, message, ingressId); +} + +void forwarder_Receive(Forwarder *forwarder, Message *message) { + parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null"); + parcAssertNotNull(message, "Parameter message must be non-null"); + + // this takes ownership of the message, so we're done here + + // this are the checks needed to implement WLDR. We set wldr only on the STAs + // and we let the AP to react according to choise of the client. + // if the STA enables wldr using the set command, the AP enable wldr as well + // otherwise, if the STA disable it the AP remove wldr + // WLDR should be enabled only on the STAs using the command line + // TODO + // disable WLDR command line on the AP + const Connection *conn = connectionTable_FindById( + forwarder->connectionTable, message_GetIngressConnectionId(message)); + + if (!conn) { + return; + } + + if (message_HasWldr(message)) { + if (connection_HasWldr(conn)) { + // case 1: WLDR is enabled + connection_DetectLosses((Connection *)conn, message); + } else if (!connection_HasWldr(conn) && + connection_WldrAutoStartAllowed(conn)) { + // case 2: We are on an AP. We enable WLDR + connection_EnableWldr((Connection *)conn); + connection_DetectLosses((Connection *)conn, message); + } + // case 3: Ignore WLDR + } else { + if (connection_HasWldr(conn) && connection_WldrAutoStartAllowed(conn)) { + // case 1: STA do not use WLDR, we disable it + connection_DisableWldr((Connection *)conn); + } + } + + messageProcessor_Receive(forwarder->processor, message); +} + +Ticks forwarder_GetTicks(const Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return parcClock_GetTime(forwarder->clock) + forwarder->clockOffset; +} + +Ticks forwarder_NanosToTicks(uint64_t nanos) { return NSEC_TO_TICKS(nanos); } + +uint64_t forwarder_TicksToNanos(Ticks ticks) { + return (1000000000ULL) * ticks / HZ; +} + +bool forwarder_AddOrUpdateRoute(Forwarder *forwarder, + add_route_command *control, unsigned ifidx) { + parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null"); + parcAssertNotNull(control, "Parameter route must be non-null"); + + // we only have one message processor + bool res = + messageProcessor_AddOrUpdateRoute(forwarder->processor, control, ifidx); + + return res; +} + +bool forwarder_RemoveRoute(Forwarder *forwarder, remove_route_command *control, + unsigned ifidx) { + parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null"); + parcAssertNotNull(control, "Parameter route must be non-null"); + + // we only have one message processor + return messageProcessor_RemoveRoute(forwarder->processor, control, ifidx); +} + +void forwarder_RemoveConnectionIdFromRoutes(Forwarder *forwarder, + unsigned connectionId) { + parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null"); + messageProcessor_RemoveConnectionIdFromRoutes(forwarder->processor, + connectionId); +} + +void forwarder_SetStrategy(Forwarder *forwarder, Name *prefix, + strategy_type strategy) { + parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null"); + parcAssertNotNull(prefix, "Parameter prefix must be non-null"); + + // if (strategy == NULL) { + // strategy = SET_STRATEGY_RANDOM; + // } + + processor_SetStrategy(forwarder->processor, prefix, strategy); +} + +FibEntryList *forwarder_GetFibEntries(Forwarder *forwarder) { + return messageProcessor_GetFibEntries(forwarder->processor); +} + +void forwarder_SetContentObjectStoreSize(Forwarder *forwarder, + size_t maximumContentStoreSize) { + messageProcessor_SetContentObjectStoreSize(forwarder->processor, + maximumContentStoreSize); +} + +void forwarder_ClearCache(Forwarder *forwarder) { + messageProcessor_ClearCache(forwarder->processor); +} + +PARCClock *forwarder_GetClock(const Forwarder *forwarder) { + return forwarder->clock; +} + +hicn_socket_helper_t *forwarder_GetHIcnSocketHelper(Forwarder *forwarder) { + return forwarder->hicnSocketHelper; +} + +// ======================================================= + +static void _signal_cb(int sig, PARCEventType events, void *user_data) { + Forwarder *forwarder = (Forwarder *)user_data; + + logger_Log(forwarder->logger, LoggerFacility_Core, PARCLogLevel_Warning, + __func__, "signal %d events %d", sig, events); + + switch ((int)sig) { + case SIGTERM: + logger_Log(forwarder->logger, LoggerFacility_Core, PARCLogLevel_Warning, + __func__, "Caught an terminate signal; exiting cleanly."); + dispatcher_Stop(forwarder->dispatcher); + break; + + case SIGINT: + logger_Log(forwarder->logger, LoggerFacility_Core, PARCLogLevel_Warning, + __func__, "Caught an interrupt signal; exiting cleanly."); + dispatcher_Stop(forwarder->dispatcher); + break; + + case SIGUSR1: + // dump stats + break; + + default: + break; + } +} + +static void _keepalive_cb(int fd, PARCEventType what, void *user_data) { + parcAssertTrue(what & PARCEventType_Timeout, "Got unexpected tick_cb: %d", + what); + // function is just a keepalive for hicn-light, does not do anything +} + +#ifdef WITH_MAPME +FIB *forwarder_getFib(Forwarder *forwarder) { + return messageProcessor_getFib(forwarder->processor); +} + +void forwarder_onConnectionAdded(Forwarder *forwarder, const Connection *conn) { + mapMe_onConnectionAdded(forwarder->mapme, conn); +} + +void forwarder_onConnectionRemoved(Forwarder *forwarder, + const Connection *conn) {} + +void forwarder_ProcessMapMe(Forwarder *forwarder, uint8_t *msgBuffer, + unsigned conn_id) { + mapMe_Process(forwarder->mapme, msgBuffer, conn_id); +} + +#endif /* WITH_MAPME */ diff --git a/hicn-light/src/core/forwarder.h b/hicn-light/src/core/forwarder.h new file mode 100755 index 000000000..ad3f9756b --- /dev/null +++ b/hicn-light/src/core/forwarder.h @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * The methods in this header are for the non-threaded forwarder. They should + * only be called within the forwarders thread of execution. + */ + +#ifndef forwarder_h +#define forwarder_h + +#include +#include + +#include +#include +#include + +#include + +#include + +#ifdef WITH_MAPME +#include +#endif /* WITH_MAPME */ + +#include +#include +#include + +#include + +#include + +#include + +#define PORT_NUMBER 9695 +#define PORT_NUMBER_AS_STRING "9695" + +#include + +// ============================================== + +struct forwarder; +typedef struct forwarder Forwarder; + + +/** + * @function forwarder_Create + * @abstract Create the forwarder and use the provided logger for diagnostic + * output + * @discussion + * If the logger is null, hicn-light will create a STDOUT logger. + * + * @param logger may be NULL + */ +Forwarder *forwarder_Create(Logger *logger); + +/** + * @function forwarder_Destroy + * @abstract Destroys the forwarder, stopping all traffic and freeing all memory + */ +void forwarder_Destroy(Forwarder **ptr); + +/** + * @function forwarder_SetupAllListeners + * @abstract Setup all listeners (tcp, udp, local, ether, ip multicast) on all + * interfaces + * @discussion + * Sets up all listeners on all running interfaces. This provides a quick and + * easy startup, rather than providing a configuration file or programmatic + * commands. + * + * @param port is used by TCP and UDP listeners, in host byte order + * @param localPath is the AF_UNIX path to use, if NULL no AF_UNIX listener is + * setup + */ +void forwarder_SetupAllListeners(Forwarder *forwarder, uint16_t port, + const char *localPath); + +/** + * Configure hicn-light via a configuration file + * + * The configuration file is a set of lines, just like used in hicnLightControl. + * You need to have "add listener" lines in the file to receive connections. No + * default listeners are configured. + * + * @param [in] forwarder An alloated Forwarder + * @param [in] filename The path to the configuration file + */ +void forwarder_SetupFromConfigFile(Forwarder *forwarder, const char *filename); + +/** + * Returns the logger used by this forwarder + * + * If you will store the logger, you should acquire a reference to it. + * + * @param [in] forwarder An allocated hicn-light forwarder + * + * @retval non-null The logger used by hicn-light + * @retval null An error + */ +Logger *forwarder_GetLogger(const Forwarder *forwarder); + +/** + * @function forwarder_SetLogLevel + * @abstract Sets the minimum level to log + */ +void forwarder_SetLogLevel(Forwarder *forwarder, PARCLogLevel level); + +/** + * @function forwarder_GetNextConnectionId + * @abstract Get the next identifier for a new connection + */ +unsigned forwarder_GetNextConnectionId(Forwarder *forwarder); + +Messenger *forwarder_GetMessenger(Forwarder *forwarder); + +Dispatcher *forwarder_GetDispatcher(Forwarder *forwarder); + +/** + * Returns the set of currently active listeners + * + * @param [in] forwarder An allocated hicn-light forwarder + * + * @retval non-null The set of active listeners + * @retval null An error + */ +ListenerSet *forwarder_GetListenerSet(Forwarder *forwarder); + +/** + * Returns the forwrder's connection table + * + * @param [in] forwarder An allocated hicn-light forwarder + * + * @retval non-null The connection tabler + * @retval null An error + * + */ +ConnectionTable *forwarder_GetConnectionTable(Forwarder *forwarder); + +/** + * Returns a Tick-based clock + * + * Runs at approximately 1 msec per tick (see HZ in forwarder.c). + * Do not Release this clock. If you save a copy of it, create your own + * reference to it with parcClock_Acquire(). + * + * @param [in] forwarder An allocated hicn-light forwarder + * + * @retval non-null An allocated hicn-light Clock based on the Tick counter + * @retval null An error + */ +PARCClock *forwarder_GetClock(const Forwarder *forwarder); + +/** + * Direct call to get the Tick clock + * + * Runs at approximately 1 msec per tick (see HZ in forwarder.c) + * + * @param [in] forwarder An allocated hicn-light forwarder + */ +Ticks forwarder_GetTicks(const Forwarder *forwarder); + +/** + * Convert nano seconds to Ticks + * + * Converts nano seconds to Ticks, based on HZ (in forwarder.c) + */ +Ticks forwarder_NanosToTicks(uint64_t nanos); + +uint64_t forwarder_TicksToNanos(Ticks ticks); + +void forwarder_ReceiveCommand(Forwarder *forwarder, command_id command, + struct iovec *message, unsigned ingressId); + +void forwarder_Receive(Forwarder *forwarder, Message *mesage); + +/** + * @function forwarder_AddOrUpdateRoute + * @abstract Adds or updates a route on all the message processors + */ +bool forwarder_AddOrUpdateRoute(Forwarder *forwarder, + add_route_command *control, unsigned ifidx); + +/** + * @function forwarder_RemoveRoute + * @abstract Removes a route from all the message processors + */ +bool forwarder_RemoveRoute(Forwarder *forwarder, remove_route_command *control, + unsigned ifidx); + +/** + * Removes a connection id from all routes + */ +void forwarder_RemoveConnectionIdFromRoutes(Forwarder *forwarder, + unsigned connectionId); + +/** + * @function forwarder_GetConfiguration + * @abstract The configuration object + * @discussion + * The configuration contains all user-issued commands. It does not include + * dynamic state. + */ +Configuration *forwarder_GetConfiguration(Forwarder *forwarder); + +FibEntryList *forwarder_GetFibEntries(Forwarder *forwarder); + +/** + * Sets the maximum number of content objects in the content store + * + * Implementation dependent - may wipe the cache. + */ +void forwarder_SetContentObjectStoreSize(Forwarder *forwarder, + size_t maximumContentStoreSize); + +void forwarder_SetChacheStoreFlag(Forwarder *forwarder, bool val); + +bool forwarder_GetChacheStoreFlag(Forwarder *forwarder); + +void forwarder_SetChacheServeFlag(Forwarder *forwarder, bool val); + +bool forwarder_GetChacheServeFlag(Forwarder *forwarder); + +void forwarder_ClearCache(Forwarder *forwarder); + +void forwarder_SetStrategy(Forwarder *forwarder, Name *prefix, + strategy_type strategy); + +hicn_socket_helper_t *forwarder_GetHIcnSocketHelper(Forwarder *forwarder); + +#ifdef WITH_MAPME + +/** + * @function forwarder_getFib + * @abstract Returns the hICN forwarder's FIB. + * @param [in] forwarder - Pointer to the hICN forwarder. + * @returns Pointer to the hICN FIB. + */ +FIB *forwarder_getFib(Forwarder *forwarder); + +/** + * @function forwarder_onConnectionAdded + * @abstract Callback fired upon addition of a new connection through the + * control protocol. + * @param [in] forwarder - Pointer to the hICN forwarder. + * @param [in] conn - Pointer to the newly added connection. + */ +void forwarder_onConnectionAdded(Forwarder *forwarder, const Connection *conn); + +/** + * @function forwarder_onConnectionRemoved + * @abstract Callback fired upon removal of a connection through the control + * protocol. + * @param [in] forwarder - Pointer to the hICN forwarder. + * @param [in] conn - Pointer to the removed connection. + */ +void forwarder_onConnectionRemoved(Forwarder *forwarder, + const Connection *conn); + +/** + * @function forwarder_ProcessMapMe + * @abstract Callback fired by an hICN listener upon reception of a MAP-Me + * message. + * @param [in] forwarder - Pointer to the hICN forwarder. + * @param [in] msgBuffer - MAP-Me buffer + * @param [in] conn_id - Ingress connection id + */ +void forwarder_ProcessMapMe(Forwarder *forwarder, uint8_t *msgBuffer, + unsigned conn_id); + +#endif /* WITH_MAPME */ + +#endif // forwarder_h diff --git a/hicn-light/src/core/logger.c b/hicn-light/src/core/logger.c new file mode 100755 index 000000000..cac3000e2 --- /dev/null +++ b/hicn-light/src/core/logger.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include +#include + +struct logger { + PARCClock *clock; + + PARCLogReporter *reporter; + PARCLog *loggerArray[LoggerFacility_END]; +}; + +static const struct facility_to_string { + LoggerFacility facility; + const char *string; +} _facilityToString[] = { + {.facility = LoggerFacility_Config, .string = "Config"}, + {.facility = LoggerFacility_Core, .string = "Core"}, + {.facility = LoggerFacility_IO, .string = "IO"}, + {.facility = LoggerFacility_Message, .string = "Message"}, + {.facility = LoggerFacility_Processor, .string = "Processor"}, + {.facility = 0, .string = NULL}}; + +const char *logger_FacilityString(LoggerFacility facility) { + for (int i = 0; _facilityToString[i].string != NULL; i++) { + if (_facilityToString[i].facility == facility) { + return _facilityToString[i].string; + } + } + return "Unknown"; +} + +static void _allocateLoggers(Logger *logger, PARCLogReporter *reporter) { + parcTrapUnexpectedStateIf( + logger->reporter != NULL, + "Trying to allocate a reporter when the previous one is not null"); + logger->reporter = parcLogReporter_Acquire(reporter); + + char hostname[255]; + int gotHostName = gethostname(hostname, 255); + if (gotHostName < 0) { + snprintf(hostname, 255, "unknown"); + } + + for (int i = 0; i < LoggerFacility_END; i++) { + logger->loggerArray[i] = parcLog_Create(hostname, logger_FacilityString(i), + "forwarder", logger->reporter); + parcLog_SetLevel(logger->loggerArray[i], PARCLogLevel_Error); + } +} + +static void _releaseLoggers(Logger *logger) { + for (int i = 0; i < LoggerFacility_END; i++) { + parcLog_Release(&logger->loggerArray[i]); + } + parcLogReporter_Release(&logger->reporter); +} + +static void _destroyer(Logger **loggerPtr) { + Logger *logger = *loggerPtr; + _releaseLoggers(logger); + parcClock_Release(&(*loggerPtr)->clock); +} + +parcObject_ExtendPARCObject(Logger, _destroyer, NULL, NULL, NULL, NULL, NULL, + NULL); + +parcObject_ImplementAcquire(logger, Logger); + +parcObject_ImplementRelease(logger, Logger); + +Logger *logger_Create(PARCLogReporter *reporter, const PARCClock *clock) { + parcAssertNotNull(reporter, "Parameter reporter must be non-null"); + parcAssertNotNull(clock, "Parameter clock must be non-null"); + + Logger *logger = parcObject_CreateAndClearInstance(Logger); + if (logger) { + logger->clock = parcClock_Acquire(clock); + _allocateLoggers(logger, reporter); + } + + return logger; +} + +void logger_SetReporter(Logger *logger, PARCLogReporter *reporter) { + parcAssertNotNull(logger, "Parameter logger must be non-null"); + + // save the log level state + PARCLogLevel savedLevels[LoggerFacility_END]; + for (int i = 0; i < LoggerFacility_END; i++) { + savedLevels[i] = parcLog_GetLevel(logger->loggerArray[i]); + } + + _releaseLoggers(logger); + + _allocateLoggers(logger, reporter); + + // restore log level state + for (int i = 0; i < LoggerFacility_END; i++) { + parcLog_SetLevel(logger->loggerArray[i], savedLevels[i]); + } +} + +void logger_SetClock(Logger *logger, PARCClock *clock) { + parcAssertNotNull(logger, "Parameter logger must be non-null"); + parcClock_Release(&logger->clock); + logger->clock = parcClock_Acquire(clock); +} + +static void _assertInvariants(const Logger *logger, LoggerFacility facility) { + parcAssertNotNull(logger, "Parameter logger must be non-null"); + parcTrapOutOfBoundsIf(facility >= LoggerFacility_END, "Invalid facility %d", + facility); +} + +void logger_SetLogLevel(Logger *logger, LoggerFacility facility, + PARCLogLevel minimumLevel) { + _assertInvariants(logger, facility); + PARCLog *log = logger->loggerArray[facility]; + parcLog_SetLevel(log, minimumLevel); +} + +bool logger_IsLoggable(const Logger *logger, LoggerFacility facility, + PARCLogLevel level) { + _assertInvariants(logger, facility); + PARCLog *log = logger->loggerArray[facility]; + return parcLog_IsLoggable(log, level); +} + +void logger_Log(Logger *logger, LoggerFacility facility, PARCLogLevel level, + const char *module, const char *format, ...) { + if (logger_IsLoggable(logger, facility, level)) { + // this is logged as the messageid + uint64_t logtime = parcClock_GetTime(logger->clock); + + // logger_IsLoggable asserted invariants so we know facility is in bounds + PARCLog *log = logger->loggerArray[facility]; + + va_list va; + va_start(va, format); + + parcLog_MessageVaList(log, level, logtime, format, va); + + va_end(va); + } +} diff --git a/hicn-light/src/core/logger.h b/hicn-light/src/core/logger.h new file mode 100755 index 000000000..e2ab7e147 --- /dev/null +++ b/hicn-light/src/core/logger.h @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file logger.h + * @brief Logger for the hicn-light forwarder + * + * A facility based logger to allow selective logging from different parts of + * hicn-light + * + */ + +#ifndef logger_h +#define logger_h + +#include +#include +#include +#include +#include + +#include + +struct logger; +typedef struct logger Logger; + +/** + * CONFIG faciilty concerns anything in the /config directory + * CORE concerns anything in the /core directory + * IO concerns anything in the /io directory (listeners, connectors, tcp, + * ethernet, etc.) PROCESSOR concerns FIB, PIT, CS MESSAGE concerns message + * events, like parsing + */ +typedef enum { + LoggerFacility_Config, + LoggerFacility_Core, + LoggerFacility_IO, + LoggerFacility_Processor, + LoggerFacility_Message, + LoggerFacility_END // sentinel value +} LoggerFacility; + +/** + * Returns a string representation of a facility + * + * Do not free the returned value. + * + * @param [in] facility The facility to change to a string + * + * @retval string A string representation of the facility + */ +const char *logger_FacilityString(LoggerFacility facility); + +/** + * Returns a string representation of a log level + * + * Do not free the returned value. + * + * @param [in] level The level to change to a string + * + * @retval string A string representation of the level + */ +const char *logger_LevelString(PARCLogLevel level); + +/** + * Create a logger that uses a given writer and clock + * + * <#Paragraphs Of Explanation#> + * + * @param [in] writer The output writer + * @param [in] clock The clock to use for log messages + * + * @retval non-null An allocated logger + * @retval null An error + */ +Logger *logger_Create(PARCLogReporter *reporter, const PARCClock *clock); + +/** + * Release logger + */ +void logger_Release(Logger **loggerPtr); + +/** + * Acquire logger + */ +Logger *logger_Acquire(const Logger *logger); + +/** + * Sets the minimum log level for a facility + * + * The default log level is ERROR. For a message to be logged, it must be of + * equal or higher log level. + * + * @param [in] logger An allocated logger + * @param [in] facility The facility to set the log level for + * @param [in] The minimum level to log + * + */ +void logger_SetLogLevel(Logger *logger, LoggerFacility facility, + PARCLogLevel minimumLevel); + +/** + * Tests if the log level would be logged + * + * If the facility would log the given level, returns true. May be used as a + * guard around expensive logging functions. + * + * @param [in] logger An allocated logger + * @param [in] facility The facility to test + * @param [in] The level to test + * + * @retval true The given facility would log the given level + * @retval false A message of the given level would not be logged + * + */ +bool logger_IsLoggable(const Logger *logger, LoggerFacility facility, + PARCLogLevel level); + +/** + * Log a message + * + * The message will only be logged if it is loggable (logger_IsLoggable returns + * true). + * + * @param [in] logger An allocated Logger + * @param [in] facility The facility to log under + * @param [in] level The log level of the message + * @param [in] module The specific module logging the message + * @param [in] format The message with varargs + * + */ +void logger_Log(Logger *logger, LoggerFacility facility, PARCLogLevel level, + const char *module, const char *format, ...); + +/** + * Switch the logger to a new reporter + * + * Will close the old reporter and re-setup the internal loggers to use the new + * reporter. All current log level settings are preserved. + * + * @param [in] logger An allocated Logger + * @param [in] reporter An allocated PARCLogReporter + */ +void logger_SetReporter(Logger *logger, PARCLogReporter *reporter); + +/** + * Set a new clock to use with the logger + * + * The logger will start getting the time (logged as the messageid) from the + * specified clock + * + * @param [in] logger An allocated Logger + * @param [in] clock An allocated PARCClock + */ +void logger_SetClock(Logger *logger, PARCClock *clock); +#endif // logger_h diff --git a/hicn-light/src/core/mapMe.c b/hicn-light/src/core/mapMe.c new file mode 100755 index 000000000..4444bcf15 --- /dev/null +++ b/hicn-light/src/core/mapMe.c @@ -0,0 +1,816 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file mapMe.c + * @brief MAP-Me : AnchorLess Producer Mobility Management. + */ + +#ifdef WITH_MAPME + +#include +#include +#include // printf + +#include +#include +#include +#include +#include // packet types +#include +#include +#include + +#include +#include +#include +#include + +#define MS2NS(x) x * 1000000 +#define T2NS(x) forwarder_TicksToNanos(x) + +#define MAPME_DEFAULT_TU 5000 /* ms */ +#define MAPME_DEFAULT_RETX 500 /* ms */ +#define MAX_RETX 3 + +#define NOT_A_NOTIFICATION false +#define NO_INGRESS 0 +#define TIMER_NO_REPEAT false + +#define DO_DISCOVERY 1 +#define MAPME_INVALID_DICOVERY_SEQ -1 + +#define LOG_FACILITY LoggerFacility_Core + +#define LOG(mapme, log_level, fmt, ...) \ + do { \ + Logger *logger = forwarder_GetLogger(mapme->forwarder); \ + if (logger_IsLoggable(logger, LOG_FACILITY, log_level)) { \ + logger_Log(logger, LOG_FACILITY, log_level, __func__, fmt, \ + ##__VA_ARGS__); \ + } \ + } while (0) + +#define WARN(mapme, fmt, ...) \ + LOG(mapme, PARCLogLevel_Warning, fmt, ##__VA_ARGS__) +#define ERROR(mapme, fmt, ...) \ + LOG(mapme, PARCLogLevel_Error, fmt, ##__VA_ARGS__) +#define INFO(mapme, fmt, ...) LOG(mapme, PARCLogLevel_Info, fmt, ##__VA_ARGS__) +#define DEBUG(mapme, fmt, ...) \ + LOG(mapme, PARCLogLevel_Debug, fmt, ##__VA_ARGS__) + +/** + * MAP-Me state data structure + */ +struct mapme { + uint32_t retx; /* ms */ + uint32_t Tu; /* ms */ + bool removeFibEntries; + + Forwarder *forwarder; +}; + +static MapMe MapMeDefault = {.retx = MAPME_DEFAULT_RETX, + .Tu = MAPME_DEFAULT_TU, + .removeFibEntries = false}; + +/******************************************************************************/ + +#include + +bool mapMe_Init(MapMe **mapme, Forwarder *forwarder) { + *mapme = malloc(sizeof(MapMe)); + if (!mapme) goto ERR_MALLOC; + + /* Internal state : set default values */ + memcpy(*mapme, &MapMeDefault, sizeof(MapMe)); + + (*mapme)->forwarder = forwarder; + + /* Install hook on Face events to onConnectionAdded */ + // see. config/configuration.c + + /* Install hook for signalization processing. See : + * - io/hicnListener.c + * - src/core/connection.{c,h} + */ + + ERROR((*mapme), "MapMe"); + + return true; + +ERR_MALLOC: + return false; +} + +/****************************************************************************** + * TFIB + ******************************************************************************/ + +#define INVALID_SEQ 0 +#define INIT_SEQ 1 + +typedef struct { + uint32_t seq; + PARCHashMap *nexthops; + /* Update/Notification heuristic */ + Ticks lastAckedUpdate; // XXX This is only for producer !!! +} MapMeTFIB; + +static MapMeTFIB *mapMeTFIB_Create() { + MapMeTFIB *tfib; + tfib = malloc(sizeof(MapMeTFIB)); + if (!tfib) goto ERR_MALLOC; + tfib->seq = 0; + tfib->lastAckedUpdate = 0; + tfib->nexthops = parcHashMap_Create(); + if (!tfib->nexthops) goto ERR_HASHMAP; + + return tfib; + +ERR_HASHMAP: + free(tfib); +ERR_MALLOC: + return NULL; +} + +void mapMeTFIB_Release(MapMeTFIB **tfibPtr) { + MapMeTFIB *tfib = *tfibPtr; + parcHashMap_Release(&tfib->nexthops); + free(tfib); + *tfibPtr = NULL; +} + +/** + * @function mapMe_CreateTFIB + * @abstract Associate a new TFIB entry to a FIB entry. + * @param [in] - Pointer to the FIB entry. + * @return Boolean indicating the success of the operation. + */ +static void mapMe_CreateTFIB(FibEntry *fibEntry) { + MapMeTFIB *tfib; + + /* Make sure we don't already have an associated TFIB entry */ + tfib = fibEntry_getUserData(fibEntry); + // assertNull(tfib); + + tfib = mapMeTFIB_Create(); + fibEntry_setUserData(fibEntry, tfib, (void (*)(void **))mapMeTFIB_Release); +} + +#define TFIB(fibEntry) ((MapMeTFIB *)fibEntry_getUserData(fibEntry)) + +static const PARCEventTimer *mapMeTFIB_Get(const MapMeTFIB *tfib, + unsigned conn_id) { + const PARCEventTimer *timer; + const PARCBuffer *buffer; + PARCUnsigned *cid = parcUnsigned_Create(conn_id); + buffer = parcHashMap_Get(tfib->nexthops, cid); + if (!buffer) return NULL; + PARCByteArray *array = parcBuffer_Array(buffer); + timer = *((PARCEventTimer **)parcByteArray_Array(array)); + parcUnsigned_Release(&cid); + return timer; +} + +static void mapMeTFIB_Put(MapMeTFIB *tfib, unsigned conn_id, + const PARCEventTimer *timer) { + /* NOTE: Timers are not objects (the only class not being an object in + * fact), and as such, we cannot use them as values for the HashMap. + * Just like for unsigned we needed the PARC wrapper. + * There is no wrapper for pointers, so we use Arrays, which has an ubly + * syntax... + */ + PARCUnsigned *cid = parcUnsigned_Create(conn_id); + PARCBuffer *buffer = + parcBuffer_CreateFromArray(&timer, sizeof(PARCEventTimer *)); + parcHashMap_Put(tfib->nexthops, cid, buffer); + parcUnsigned_Release(&cid); + parcBuffer_Release(&buffer); +} + +static void mapMeTFIB_Remove(MapMeTFIB *tfib, unsigned conn_id) { + // Who releases the timer ? + PARCUnsigned *cid = parcUnsigned_Create(conn_id); + parcHashMap_Remove(tfib->nexthops, cid); + parcUnsigned_Release(&cid); +} + +static PARCIterator *mapMeTFIB_CreateKeyIterator(const MapMeTFIB *tfib) { + return parcHashMap_CreateKeyIterator(tfib->nexthops); +} + +int hicn_prefix_from_name(const Name *name, hicn_prefix_t *prefix) { + NameBitvector *bv = name_GetContentName(name); + ip_address_t ip_address; + nameBitvector_ToIPAddress(bv, &ip_address); + + /* The name length will be equal to ip address' prefix length */ + return hicn_prefix_create_from_ip_address(&ip_address, prefix); +} + +static Message *mapMe_createMessage(const MapMe *mapme, const Name *name, + mapme_params_t *params) { + Ticks now = forwarder_GetTicks(mapme->forwarder); + Logger *logger = logger_Acquire(forwarder_GetLogger(mapme->forwarder)); + + INFO(mapme, "[MAP-Me] CreateMessage type=%d seq=%d", params->type, + params->seq); + + size_t size = (params->protocol == IPPROTO_IPV6) ? HICN_MAPME_V6_HDRLEN + : HICN_MAPME_V4_HDRLEN; + uint8_t *icmp_pkt = parcMemory_AllocateAndClear(size); + + hicn_prefix_t prefix; + int rc = hicn_prefix_from_name(name, &prefix); + if (rc < 0) { + ERROR(mapme, "[MAP-Me] Failed to create lib's name"); + goto ERR_NAME; + } + + INFO(mapme, "[MAP-Me] Creating MAP-Me packet"); + size_t len = hicn_mapme_create_packet(icmp_pkt, &prefix, params); + if (len != 0) { + ERROR(mapme, "[MAP-Me] Failed to create mapme packet through lib"); + goto ERR_CREATE; + } + + // hicn_packet_dump(icmp_pkt, MAPME_HDRLEN); + + return message_CreateFromByteArray(NO_INGRESS, icmp_pkt, + MessagePacketType_Interest, now, logger); + +ERR_CREATE: +ERR_NAME: + return NULL; +} + +static Message *mapMe_createAckMessage(const MapMe *mapme, + const uint8_t *msgBuffer, + const mapme_params_t *params) { + Ticks now = forwarder_GetTicks(mapme->forwarder); + Logger *logger = logger_Acquire(forwarder_GetLogger(mapme->forwarder)); + + size_t size = (params->protocol == IPPROTO_IPV6) ? HICN_MAPME_V6_HDRLEN + : HICN_MAPME_V4_HDRLEN; + uint8_t *icmp_pkt = parcMemory_AllocateAndClear(size); + memcpy(icmp_pkt, msgBuffer, size); + + size_t len = hicn_mapme_create_ack(icmp_pkt, params); + if (len != size) { + ERROR(mapme, "[MAP-Me] Failed to create mapme ack packet through lib"); + return NULL; + } + + return message_CreateFromByteArray( + NO_INGRESS, icmp_pkt, MessagePacketType_ContentObject, now, logger); +} + +struct setFacePendingArgs { + const MapMe *mapme; + const Name *name; + FibEntry *fibEntry; + unsigned conn_id; + bool send; + bool is_first; + uint32_t num_retx; +}; + +static bool mapMe_setFacePending(const MapMe *mapme, const Name *name, + FibEntry *fibEntry, unsigned conn_id, + bool send, bool is_first, uint32_t num_retx); + +static void mapMe_setFacePendingCallback(int fd, PARCEventType which_event, + void *data) { + struct setFacePendingArgs *args = (struct setFacePendingArgs *)data; + + parcAssertTrue(which_event & PARCEventType_Timeout, + "Event incorrect, expecting %X set, got %X", + PARCEventType_Timeout, which_event); + + INFO(args->mapme, "Timeout during retransmission. Re-sending"); + mapMe_setFacePending(args->mapme, args->name, args->fibEntry, args->conn_id, + args->send, args->is_first, args->num_retx); +} + +/** + * @brief Update/Notification heuristic: + * + * NOTE: IN are currently disabled until the proper placeholder is agreed in the + * interest header. + */ +static hicn_mapme_type_t mapMe_getTypeFromHeuristic(const MapMe *mapme, + FibEntry *fibEntry) { +#if 0 /* interplay of IU/IN */ + if (TFIB(fibEntry)->lastAckedUpdate == 0) { + return UPDATE; + } else { + Ticks interval = now - TFIB(fibEntry)->lastAckedUpdate; + return (T2NS(interval) > MS2NS(mapme->Tu)) ? UPDATE : NOTIFICATION; + } +#else /* Always send IU */ + return UPDATE; +#endif +} + +static bool mapMe_setFacePending(const MapMe *mapme, const Name *name, + FibEntry *fibEntry, unsigned conn_id, + bool send, bool is_first, uint32_t num_retx) { + int rc; + + INFO(mapme, "[MAP-Me] SetFacePending connection=%d prefix=XX retx=%d", + conn_id, num_retx); + + /* NOTE: if the face is pending an we receive an IN, maybe we should not + * cancel the timer + */ + Dispatcher *dispatcher = forwarder_GetDispatcher(mapme->forwarder); + PARCEventTimer *timer; + + // NOTE + // - at producer, send always true, we always send something reliably so we + // set the timer. + // - in the network, we always forward an IU, and never an IN + if (is_first || send) { + // XXX + mapme_params_t params = { + .protocol = IPPROTO_IPV6, + .type = is_first ? mapMe_getTypeFromHeuristic(mapme, fibEntry) : UPDATE, + .seq = TFIB(fibEntry)->seq}; + Message *special_interest = mapMe_createMessage(mapme, name, ¶ms); + if (!special_interest) { + INFO(mapme, "[MAP-Me] Could not create special interest"); + return false; + } + + const ConnectionTable *table = + forwarder_GetConnectionTable(mapme->forwarder); + const Connection *conn = + connectionTable_FindById((ConnectionTable *)table, conn_id); + if (conn) { + INFO(mapme, "[MAP-Me] Sending MAP-Me packet"); + connection_ReSend(conn, special_interest, NOT_A_NOTIFICATION); + } else { + INFO(mapme, "[MAP-Me] Stopped retransmissions as face went down"); + } + + if (num_retx < MAX_RETX) { + INFO(mapme, "[MAP-Me] - Scheduling retransmission\n"); + /* Schedule retransmission */ + struct setFacePendingArgs *args = + malloc(sizeof(struct setFacePendingArgs)); + if (!args) goto ERR_MALLOC; + args->mapme = mapme; + args->name = name; + args->fibEntry = fibEntry; + args->conn_id = conn_id; + args->send = send; + args->is_first = is_first; + args->num_retx = num_retx + 1; + + timer = dispatcher_CreateTimer(dispatcher, TIMER_NO_REPEAT, + mapMe_setFacePendingCallback, args); + struct timeval timeout = {mapme->retx / 1000, + (mapme->retx % 1000) * 1000}; + rc = parcEventTimer_Start(timer, &timeout); + if (rc < 0) goto ERR_TIMER; + } else { + INFO(mapme, "[MAP-Me] Last retransmission."); + timer = NULL; + } + } else { + INFO(mapme, "[MAP-Me] - not forwarding as send is False"); + timer = NULL; + } + + PARCEventTimer *oldTimer = + (PARCEventTimer *)mapMeTFIB_Get(TFIB(fibEntry), conn_id); + if (oldTimer) { + INFO(mapme, "[MAP-Me] - Found old timer, would need to cancel !"); + // parcEventTimer_Stop(oldTimer); + } + INFO(mapme, "[MAP-Me] - Putting new timer in TFIB"); + if (timer) mapMeTFIB_Put(TFIB(fibEntry), conn_id, timer); + + return true; + +ERR_MALLOC: +ERR_TIMER: + return false; +} + +/*------------------------------------------------------------------------------ + * Event handling + *----------------------------------------------------------------------------*/ + +/* + * Return true if we have at least one local connection as next hop + */ +static bool mapMe_hasLocalNextHops(const MapMe *mapme, + const FibEntry *fibEntry) { + const NumberSet *nexthops = fibEntry_GetNexthops(fibEntry); + const ConnectionTable *table = forwarder_GetConnectionTable(mapme->forwarder); + + for (size_t j = 0; j < fibEntry_NexthopCount(fibEntry); j++) { + /* Retrieve Nexthop #j */ + unsigned conn_id = numberSet_GetItem(nexthops, j); + const Connection *conn = + connectionTable_FindById((ConnectionTable *)table, conn_id); + + /* Ignore non-local connections */ + if (!connection_IsLocal(conn)) continue; + /* We don't need to test against conn_added since we don't + * expect it to have any entry in the FIB */ + + return true; + } + return false; +} + +/* + * Callback called everytime a new connection is created by the control protocol + */ +void mapMe_onConnectionAdded(const MapMe *mapme, const Connection *conn_added) { + /* bool ret; */ + FibEntryList *fiblist; + + /* Ignore local connections corresponding to applications for now */ + if (connection_IsLocal(conn_added)) return; + + unsigned conn_added_id = connection_GetConnectionId(conn_added); + INFO(mapme, "[MAP-Me] New connection %d", conn_added_id); + + /* + * Iterate on FIB to find locally served prefix + * Ideally, we want to avoid a FIB scan everytime a face is added/removed + */ + fiblist = forwarder_GetFibEntries(mapme->forwarder); + for (size_t i = 0; i < fibEntryList_Length(fiblist); i++) { + FibEntry *fibEntry = (FibEntry *)fibEntryList_Get(fiblist, i); + const Name *name = fibEntry_GetPrefix(fibEntry); + + /* Skip entries that have no local connection as next hop */ + if (!mapMe_hasLocalNextHops(mapme, fibEntry)) continue; + + /* This entry corresponds to a locally served prefix, set + * Special Interest */ + if (!TFIB(fibEntry)) /* Create TFIB associated to FIB entry */ + mapMe_CreateTFIB(fibEntry); + TFIB(fibEntry)->seq++; + + char *name_str = name_ToString(name); + INFO(mapme, "[MAP-Me] sending IU/IN for name %s on connection %d", name_str, + conn_added_id); + free(name_str); + + mapMe_setFacePending(mapme, name, fibEntry, conn_added_id, true, true, 0); + } +} + +/*------------------------------------------------------------------------------ + * Special Interest handling + *----------------------------------------------------------------------------*/ + +/** + * @discussion This function is way too long and should be cut out + */ +static bool mapMe_onSpecialInterest(const MapMe *mapme, + const uint8_t *msgBuffer, + unsigned conn_in_id, hicn_prefix_t *prefix, + mapme_params_t *params) { + const ConnectionTable *table = forwarder_GetConnectionTable(mapme->forwarder); + /* The cast is needed since connectionTable_FindById miss the + * const qualifier for the first parameter */ + const Connection *conn_in = + connectionTable_FindById((ConnectionTable *)table, conn_in_id); + seq_t fibSeq, seq = params->seq; + bool send = (params->type == UPDATE); + bool rv; + + Name *name = name_CreateFromPacket(msgBuffer, MessagePacketType_Interest); + char *name_str = name_ToString(name); + INFO(mapme, + "[MAP-Me] Ack'ed Special Interest on connection %d - prefix=%s type=XX " + "seq=%d", + conn_in_id, name_str, seq); + free(name_str); + + /* + * Immediately send an acknowledgement back on the ingress connection + * We always ack, even duplicates. + */ + Message *ack = mapMe_createAckMessage(mapme, msgBuffer, params); + if (!ack) goto ERR_ACK_CREATE; + rv = connection_ReSend(conn_in, ack, NOT_A_NOTIFICATION); + if (!rv) goto ERR_ACK_SEND; + message_Release(&ack); + + /* EPM on FIB */ + /* only the processor has access to the FIB */ + FIB *fib = forwarder_getFib(mapme->forwarder); + + FibEntry *fibEntry = fib_Contains(fib, name); + if (!fibEntry) { + INFO(mapme, + "[MAP-Me] - Re-creating FIB entry with next hop on connection %d", + conn_in_id); + /* + * This might happen for a node hosting a producer which has moved. + * Destroying the face has led to removing all corresponding FIB + * entries. In that case, we need to correctly restore the FIB entries. + */ + strategy_type fwdStrategy = LAST_STRATEGY_VALUE; + + fibEntry = fibEntry_Create(name, fwdStrategy); + fibEntry_AddNexthopByConnectionId(fibEntry, conn_in_id); + mapMe_CreateTFIB(fibEntry); + TFIB(fibEntry)->seq = seq; // INIT_SEQ; + fib_Add(fib, fibEntry); + return true; // with proper seq, we are done + + } else if (!TFIB(fibEntry)) { + /* Create TFIB associated to FIB entry */ + INFO(mapme, + "[MAP-Me] - Creating TFIB entry with default sequence number"); + mapMe_CreateTFIB(fibEntry); + } + + fibSeq = TFIB(fibEntry)->seq; + if (seq > fibSeq) { + INFO(mapme, + "[MAP-Me] - Higher sequence number than FIB %d, updating seq and " + "next hops", + fibSeq); + /* This has to be done first to allow processing SpecialInterestAck's */ + TFIB(fibEntry)->seq = seq; + + /* Reliably forward the IU on all prevHops */ + INFO(mapme, "[MAP-Me] - (1/3) processing prev hops"); + if (params->type == UPDATE) { + PARCIterator *iterator = mapMeTFIB_CreateKeyIterator(TFIB(fibEntry)); + while (parcIterator_HasNext(iterator)) { + PARCUnsigned *cid = parcIterator_Next(iterator); + unsigned conn_id = parcUnsigned_GetUnsigned(cid); + INFO(mapme, "[MAP-Me] - Re-sending IU to pending connection %d", + conn_id); + mapMe_setFacePending(mapme, fibEntry_GetPrefix(fibEntry), fibEntry, + conn_id, false, false, 0); + } + parcIterator_Release(&iterator); + } + + /* nextHops -> prevHops + * + * We add to the list of pendingUpdates the current next hops, and + * eventually forward them an IU too. + * + * Exception: nextHops -> nextHops + * Because of retransmission issues, it is possible that a second interest + * (with same of higher sequence number) is receive from a next-hop + * interface. In that case, the face remains a next hop. + */ + const NumberSet *nexthops_old = fibEntry_GetNexthops(fibEntry); + + /* We make a copy to be able to send IU _after_ updating next hops */ + NumberSet *nexthops = numberSet_Create(); + numberSet_AddSet(nexthops, nexthops_old); + + /* We are considering : * -> nextHops + * + * If inFace was a previous hop, we need to cancel the timer and remove + * the entry. Also, the face should be added to next hops. + * + * Optimization : nextHops -> nextHops + * - no next hop to add + * - we know that inFace was not a previous hop since it was a next hop and + * this forms a partition. No need for a search + */ + + INFO(mapme, "[MAP-Me] - (3/3) next hops ~~> prev hops"); + PARCEventTimer *oldTimer = + (PARCEventTimer *)mapMeTFIB_Get(TFIB(fibEntry), conn_in_id); + if (oldTimer) { + /* This happens if we receive an IU while we are still sending + * one in the other direction + */ + INFO(mapme, "[MAP-Me] - Canceled pending timer"); + parcEventTimer_Stop(oldTimer); + mapMeTFIB_Remove(TFIB(fibEntry), conn_in_id); + } + + /* Remove all next hops */ + for (size_t k = 0; k < numberSet_Length(nexthops_old); k++) { + unsigned conn_id = numberSet_GetItem(nexthops_old, k); + INFO(mapme, "[MAP-Me] - Replaced next hops by connection %d", conn_id); + fibEntry_RemoveNexthopByConnectionId(fibEntry, conn_id); + } + fibEntry_AddNexthopByConnectionId(fibEntry, conn_in_id); + + INFO(mapme, "[MAP-Me] - (2/3) processing next hops"); + bool complete = true; + for (size_t k = 0; k < numberSet_Length(nexthops); k++) { + unsigned conn_id = numberSet_GetItem(nexthops, k); + INFO(mapme, " - Next hop connection %d", conn_id); + if (conn_id == conn_in_id) { + INFO(mapme, " . Ignored this next hop since equal to ingress face"); + continue; + } + + INFO(mapme, "[MAP-Me] - Sending IU on current next hop connection %d", + conn_id); + mapMe_setFacePending(mapme, fibEntry_GetPrefix(fibEntry), fibEntry, + conn_id, send, false, 0); + complete = false; + } + + /* + * The update is completed when the IU could not be sent to any + * other next hop. + */ + if (complete) INFO(mapme, "[MAP-Me] - Update completed !"); + + numberSet_Release(&nexthops); + + } else if (seq == fibSeq) { + /* + * Multipath, multihoming, multiple producers or duplicate interest + * + * In all cases, we assume the propagation was already done when the first + * interest with the same sequence number was received, so we stop here + * + * It might happen that the previous AP has still a connection to the + * producer and that we received back our own IU. In that case, we just + * need to Ack and ignore it. + */ + if (mapMe_hasLocalNextHops(mapme, fibEntry)) { + INFO(mapme, "[MAP-Me] - Received original interest... Update complete"); + return true; + } + + INFO(mapme, "[MAP-Me] - Adding multipath next hop on connection %d", + conn_in_id); + fibEntry_AddNexthopByConnectionId(fibEntry, conn_in_id); + + } else { // seq < fibSeq + /* + * Face is propagating outdated information, we can just + * consider it as a prevHops. Send the special interest backwards with + * the new sequence number to reconciliate this outdated part of the + * arborescence. + */ + INFO( + mapme, + "[MAP-Me] - Update interest %d -> %d sent backwards on connection %d", + seq, fibSeq, conn_in_id); + mapMe_setFacePending(mapme, fibEntry_GetPrefix(fibEntry), fibEntry, + conn_in_id, send, false, 0); + } + + return true; + +ERR_ACK_SEND: + message_Release(&ack); +ERR_ACK_CREATE: + return false; +} + +void mapMe_onSpecialInterestAck(const MapMe *mapme, const uint8_t *msgBuffer, + unsigned conn_in_id, hicn_prefix_t *prefix, + mapme_params_t *params) { + INFO(mapme, "[MAP-Me] Receive IU/IN Ack on connection %d", conn_in_id); + + const Name *name = + name_CreateFromPacket(msgBuffer, MessagePacketType_Interest); + + FIB *fib = forwarder_getFib(mapme->forwarder); + FibEntry *fibEntry = fib_Contains(fib, name); + parcAssertNotNull(fibEntry, + "No corresponding FIB entry for name contained in IU Ack"); + + /* Test if the latest pending update has been ack'ed, otherwise just ignore */ + seq_t seq = params->seq; + if (seq != INVALID_SEQ) { + seq_t fibSeq = TFIB(fibEntry)->seq; + + if (seq < fibSeq) { + INFO(mapme, + "[MAP-Me] - Ignored special interest Ack with seq=%u, expected %u", + seq, fibSeq); + return; + } + } + + /* + * Ignore the Ack if no TFIB is present, or it has no corresponding entry + * with the ingress face. + * Note: previously, we were creating the TFIB entry + */ + if (!TFIB(fibEntry)) { + INFO(mapme, "[MAP-Me] - Ignored ACK for prefix with no TFIB entry"); + return; + } + + PARCEventTimer *timer = + (PARCEventTimer *)mapMeTFIB_Get(TFIB(fibEntry), conn_in_id); + if (!timer) { + INFO(mapme, + "[MAP-Me] - Ignored ACK for prefix not having the Connection in " + "TFIB entry. Possible duplicate ?"); + return; + } + + /* Stop timer and remove entry from TFIB */ + parcEventTimer_Stop(timer); + mapMeTFIB_Remove(TFIB(fibEntry), conn_in_id); + + INFO(mapme, "[MAP-Me] - Removing TFIB entry for ack on connection %d", + conn_in_id); + + /* We need to update the timestamp only for IU Acks, not for IN Acks */ + if (params->type == UPDATE_ACK) { + INFO(mapme, "[MAP-Me] - Updating LastAckedUpdate"); + TFIB(fibEntry)->lastAckedUpdate = forwarder_GetTicks(mapme->forwarder); + } +} + +/*----------------------------------------------------------------------------- + * Overloaded functions + *----------------------------------------------------------------------------*/ + +/* + * @abstract returns where to forward a normal interests(nexthops) defined by + * mapme, it also set the sequnence number properly if needed + */ + +/****************************************************************************** + * Public functions (exposed in the .h) + ******************************************************************************/ + +/* + * Returns true iif the message corresponds to a MAP-Me packet + */ +bool mapMe_isMapMe(const uint8_t *msgBuffer) { + uint8_t next_header = messageHandler_NextHeaderType(msgBuffer); + + const uint8_t *icmp_ptr; + if (next_header == IPPROTO_ICMP) { + icmp_ptr = msgBuffer + IPV4_HDRLEN; + } else if (next_header == IPPROTO_ICMPV6) { + icmp_ptr = msgBuffer + IPV6_HDRLEN; + } else { + return false; + } + + uint8_t type = ((_icmp_header_t *)icmp_ptr)->type; + uint8_t code = ((_icmp_header_t *)icmp_ptr)->code; + if (HICN_IS_MAPME(type, code)) return true; + + return false; +} + +/** + * @discussion The exact type of the MapMe message is determined after + * reception. In hICN, Interest Update and Notifications look like regular + * Interest packets, and are first punted from the normal path by the forwarder, + * then treated as such in the Listener to reach this function. Acknowledgements + * are received as Content (Data) packets and will land here too. + * + * This function is in charge of abstracting the low-level implementation of + * MAP-Me (eg. ICMP packets) and return higher level messages that can be + * processed by MAP-Me core. + */ +void mapMe_Process(const MapMe *mapme, const uint8_t *msgBuffer, + unsigned conn_id) { + hicn_prefix_t prefix; + mapme_params_t params; + hicn_mapme_parse_packet(msgBuffer, &prefix, ¶ms); + + // XXX Dispatch message dependenging on type + switch (params.type) { + case UPDATE: + case NOTIFICATION: + mapMe_onSpecialInterest(mapme, msgBuffer, conn_id, &prefix, ¶ms); + break; + case UPDATE_ACK: + case NOTIFICATION_ACK: + mapMe_onSpecialInterestAck(mapme, msgBuffer, conn_id, &prefix, ¶ms); + break; + default: + printf("E:Unknown message\n"); + break; + } +} + +#endif /* WITH_MAPME */ diff --git a/hicn-light/src/core/mapMe.h b/hicn-light/src/core/mapMe.h new file mode 100755 index 000000000..39edd0bd7 --- /dev/null +++ b/hicn-light/src/core/mapMe.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file mapMe.h + * @brief MAP-Me : AnchorLess Producer Mobility Management + */ + +#ifndef mapMe_h +#define mapMe_h + +#ifdef WITH_MAPME + +#include +#include + +#include + +#include +#include + +struct mapme; +typedef struct mapme MapMe; + +/** + * @function MapMe_Init + * @abstract Initializes MAP-Me state in the forwarder. + * @return bool - Boolean informing about the success of MAP-Me initialization. + */ +bool mapMe_Init(MapMe **mapme, Forwarder *Forwarder); + +/** + * @function messageHandler_isMapMe + * @abstract Identifies MAP-Me messages + * @discussion This function can be used by the forwarder to dispatch MAP-Me + * message to the appropriate processing function. Ideally this would be + * done through hooks defined in the Init function. + * @param [in] msgBuffer - The buffer to match + * @return A boolean indicating whether message is a MAP-Me control message. + */ +bool mapMe_isMapMe(const uint8_t *msgBuffer); + +/** + * @function mapMe_handleMapMeMessage + * @abstract Process a MAP-Me message. + * @param [in] mapme - Pointer to the MAP-Me data structure. + * @param [in] message - MAP-Me buffer + * @param [in] conn_id - Ingress connection id + */ +void mapMe_Process(const MapMe *mapme, const uint8_t *msgBuffer, + unsigned conn_id); + +/** + * @function mapMe_onConnectionAdded + * @abstract Callback following the addition of the face though the control + * protocol. + * @discussion This callback triggers the sending of control packets by MAP-Me. + * @param [in] mapme - Pointer to the MAP-Me data structure. + * @param [in] conn - The newly added connection. + */ +void mapMe_onConnectionAdded(const MapMe *mapme, const Connection *conn); + +/** + * @function mapMe_getNextHops + * @abstract return the nexthops to forward interests defined by mapme, it + * covers also the case where local discovery mechanisms are trriggered. + */ +NumberSet *mapMe_getNextHops(const MapMe *mapme, FibEntry *fibEntry, + const Message *interest); + +hicn_mapme_type_t mapMe_PktType_To_LibHicnPktType(MessagePacketType type); + +MessagePacketType mapMe_LibHicnPktType_To_PktType(hicn_mapme_type_t type); + +#endif /* WITH_MAPME */ + +#endif // mapMe_h diff --git a/hicn-light/src/core/message.c b/hicn-light/src/core/message.c new file mode 100755 index 000000000..6c0e916d2 --- /dev/null +++ b/hicn-light/src/core/message.c @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +struct message { + Logger *logger; + + Ticks receiveTime; + unsigned ingressConnectionId; + + Name *name; + + uint8_t *messageHead; + + unsigned length; + + uint8_t packetType; + + unsigned refcount; +}; + +Message *message_Acquire(const Message *message) { + Message *copy = (Message *)message; + copy->refcount++; + return copy; +} + +Message *message_CreateFromEventBuffer(PARCEventBuffer *data, size_t dataLength, + unsigned ingressConnectionId, + Ticks receiveTime, Logger *logger) { + // used by applications, we can get only interest or data packets + Message *message = parcMemory_AllocateAndClear(sizeof(Message)); + parcAssertNotNull(message, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Message)); + + message->logger = logger_Acquire(logger); + message->receiveTime = receiveTime; + message->ingressConnectionId = ingressConnectionId; + message->length = dataLength; + + message->messageHead = parcMemory_AllocateAndClear(dataLength); + parcAssertNotNull(message->messageHead, + "parcMemory_AllocateAndClear(%zu) returned NULL", + dataLength); + + // copy the data because *data is destroyed in the connection. + int res = parcEventBuffer_Read(data, message->messageHead, dataLength); + if (res == -1) { + return NULL; + } + + if (messageHandler_IsInterest(message->messageHead)) { + message->packetType = MessagePacketType_Interest; + } else if (messageHandler_IsData(message->messageHead)) { + message->packetType = MessagePacketType_ContentObject; + } else { + printf("Got a packet that is not a data nor an interest, drop it!\n"); + return NULL; + } + message->name = + name_CreateFromPacket(message->messageHead, message->packetType); + + message->refcount = 1; + + return message; +} + +Message *message_CreateFromByteArray(unsigned connid, uint8_t *pckt, + MessagePacketType type, Ticks receiveTime, + Logger *logger) { + Message *message = parcMemory_AllocateAndClear(sizeof(Message)); + parcAssertNotNull(message, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Message)); + + message->logger = logger_Acquire(logger); + message->receiveTime = receiveTime; + message->ingressConnectionId = connid; + message->messageHead = pckt; + message->length = messageHandler_GetTotalPacketLength(pckt); + message->packetType = type; + + if (messageHandler_IsWldrNotification(pckt)) { + message->name = NULL; + } else { + message->name = + name_CreateFromPacket(message->messageHead, message->packetType); + } + + message->refcount = 1; + + return message; +} + +void message_Release(Message **messagePtr) { + parcAssertNotNull(messagePtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*messagePtr, + "Parameter must dereference to non-null pointer"); + + Message *message = *messagePtr; + parcAssertTrue( + message->refcount > 0, + "Invalid state: message_Release called on message with 0 references %p", + (void *)message); + + message->refcount--; + if (message->refcount == 0) { + if (logger_IsLoggable(message->logger, LoggerFacility_Message, + PARCLogLevel_Debug)) { + logger_Log(message->logger, LoggerFacility_Message, PARCLogLevel_Debug, + __func__, "Message %p destroyed", (void *)message); + } + + logger_Release(&message->logger); + if (message->name != NULL) name_Release(&message->name); + parcMemory_Deallocate((void **)&message->messageHead); + parcMemory_Deallocate((void **)&message); + } + *messagePtr = NULL; +} + +bool message_Write(PARCEventQueue *parcEventQueue, const Message *message) { + parcAssertNotNull(message, "Message parameter must be non-null"); + parcAssertNotNull(parcEventQueue, "Buffer parameter must be non-null"); + + return parcEventQueue_Write(parcEventQueue, message->messageHead, + message_Length(message)); +} + +size_t message_Length(const Message *message) { + parcAssertNotNull(message, "Parameter must be non-null"); + return message->length; +} + +bool message_HasWldr(const Message *message) { + parcAssertNotNull(message, "Parameter must be non-null"); + return messageHandler_HasWldr(message->messageHead); +} + +bool message_IsWldrNotification(const Message *message) { + parcAssertNotNull(message, "Parameter must be non-null"); + return messageHandler_IsWldrNotification(message->messageHead); +} + +void message_ResetWldrLabel(Message *message) { + parcAssertNotNull(message, "Parameter must be non-null"); + return messageHandler_ResetWldrLabel(message->messageHead); +} + +unsigned message_GetWldrLabel(const Message *message) { + parcAssertNotNull(message, "Parameter must be non-null"); + return messageHandler_GetWldrLabel(message->messageHead); +} + +unsigned message_GetWldrExpectedLabel(const Message *message) { + parcAssertNotNull(message, "Parameter must be non-null"); + return messageHandler_GetExpectedWldrLabel(message->messageHead); +} + +unsigned message_GetWldrLastReceived(const Message *message) { + parcAssertNotNull(message, "Parameter must be non-null"); + return messageHandler_GetWldrLastReceived(message->messageHead); +} + +void message_SetWldrLabel(Message *message, uint16_t label) { + parcAssertNotNull(message, "Parameter must be non-null"); + messageHandler_SetWldrLabel(message->messageHead, label); +} + +Message *message_CreateWldrNotification(Message *original, uint16_t expected, + uint16_t lastReceived) { + parcAssertNotNull(original, "Parameter original must be non-null"); + Message *message = parcMemory_AllocateAndClear(sizeof(Message)); + parcAssertNotNull(message, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Message)); + message->receiveTime = original->receiveTime; + message->ingressConnectionId = original->ingressConnectionId; + message->refcount = 1; + message->logger = logger_Acquire(original->logger); + + message->length = messageHandler_GetICMPPacketSize( + messageHandler_GetIPPacketType(original->messageHead)); + message->messageHead = parcMemory_AllocateAndClear(message->length); + parcAssertNotNull(message->messageHead, + "parcMemory_AllocateAndClear returned NULL"); + + message->packetType = MessagePacketType_WldrNotification; + message->name = NULL; // nobody will use the name in a notification packet, + // so we can simply set it to NULL + + // set notification stuff. + messageHandler_SetWldrNotification( + message->messageHead, original->messageHead, expected, lastReceived); + // XXX: what about the checksum? + return message; +} + +unsigned message_GetIngressConnectionId(const Message *message) { + parcAssertNotNull(message, "Parameter must be non-null"); + return message->ingressConnectionId; +} + +void message_SetIngressConnectionId(Message *message, unsigned conn) { + parcAssertNotNull(message, "Parameter must be non-null"); + message->ingressConnectionId = conn; +} + +Ticks message_GetReceiveTime(const Message *message) { + parcAssertNotNull(message, "Parameter must be non-null"); + return message->receiveTime; +} + +uint32_t message_GetPathLabel(const Message *message) { + parcAssertNotNull(message, "Parameter must be non-null"); + return messageHandler_GetPathLabel(message->messageHead); +} + +void message_SetPathLabel(Message *message, uint32_t label) { + parcAssertNotNull(message, "Parameter must be non-null"); + return messageHandler_SetPathLabel(message->messageHead, label); +} + +void message_UpdatePathLabel(Message *message, uint8_t outFace) { + parcAssertNotNull(message, "Parameter must be non-null"); + messageHandler_UpdatePathLabel(message->messageHead, outFace); +} + +void message_ResetPathLabel(Message *message) { + parcAssertNotNull(message, "Parameter must be non-null"); + messageHandler_ResetPathLabel(message->messageHead); +} + +MessagePacketType message_GetType(const Message *message) { + parcAssertNotNull(message, "Parameter message must be non-null"); + return message->packetType; +} + +Name *message_GetName(const Message *message) { + parcAssertNotNull(message, "Parameter message must be non-null"); + return message->name; +} + +bool message_HasInterestLifetime(const Message *message) { + parcAssertNotNull(message, "Parameter message must be non-null"); + return messageHandler_HasInterestLifetime(message->messageHead); +} + +uint64_t message_GetInterestLifetimeTicks(const Message *message) { + parcAssertNotNull(message, "Parameter message must be non-null"); + uint64_t lifetime = messageHandler_GetInterestLifetime(message->messageHead); + return forwarder_NanosToTicks(lifetime * 1000000ULL); +} + +bool message_HasContentExpiryTime(const Message *message) { + parcAssertNotNull(message, "Parameter message must be non-null"); + return messageHandler_HasContentExpiryTime(message->messageHead); +} + +uint64_t message_GetContentExpiryTimeTicks(const Message *message) { + parcAssertNotNull(message, "Parameter message must be non-null"); + uint64_t expire = messageHandler_GetContentExpiryTime(message->messageHead); + return message->receiveTime + forwarder_NanosToTicks(expire * 1000000ULL); +} + +const uint8_t *message_FixedHeader(const Message *message) { + parcAssertNotNull(message, "Parameter message must be non-null"); + return message->messageHead; +} diff --git a/hicn-light/src/core/message.h b/hicn-light/src/core/message.h new file mode 100755 index 000000000..88aa32480 --- /dev/null +++ b/hicn-light/src/core/message.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file message.h + * @brief Message is the unit of forwarding, i.e. the packets being switched + * + */ +#ifndef message_h +#define message_h + +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include + +struct message; +typedef struct message Message; + +/** + * @function message_CreateFromBuffer + * @abstract Takes ownership of the input buffer, which comprises one complete + * message + */ + +Message *message_CreateFromEventBuffer(PARCEventBuffer *data, size_t dataLength, + unsigned ingressConnectionId, + Ticks receiveTime, Logger *logger); + +/** + * @function message_CreateFromByteArray + * @abstract create a message from a byte array + */ + +Message *message_CreateFromByteArray(unsigned connid, uint8_t *pckt, + MessagePacketType type, Ticks receiveTime, + Logger *logger); + +/** + * @function message_Copy + * @abstract Get a reference counted copy + */ + +Message *message_Acquire(const Message *message); + +/** + * Releases the message and frees the memory + */ +void message_Release(Message **messagePtr); + +/** + * Writes the message to the queue + */ + +bool message_Write(PARCEventQueue *parcEventQueue, const Message *message); + +/** + * Returns the total byte length of the message + */ +size_t message_Length(const Message *message); + +bool message_HasWldr(const Message *message); + +bool message_IsWldrNotification(const Message *message); + +void message_ResetWldrLabel(Message *message); + +unsigned message_GetWldrLabel(const Message *message); + +unsigned message_GetWldrExpectedLabel(const Message *message); + +unsigned message_GetWldrLastReceived(const Message *message); + +void message_SetWldrLabel(Message *message, uint16_t label); + +Message *message_CreateWldrNotification(Message *original, uint16_t expected, + uint16_t lastReceived); +/** + * Returns the connection id of the packet input + */ +unsigned message_GetIngressConnectionId(const Message *message); + +void message_SetIngressConnectionId(Message *message, unsigned conn); + +/** + * Returns the receive time (in router ticks) of the message + */ +Ticks message_GetReceiveTime(const Message *message); + +/** + * Returns the PacketType + */ +MessagePacketType message_GetType(const Message *message); + +uint32_t message_GetPathLabel(const Message *message); +void message_SetPathLabel(Message *message, uint32_t label); +void message_UpdatePathLabel(Message *message, uint8_t outFace); +void message_ResetPathLabel(Message *message); + +// =========================================================== +// Accessors used to index and compare messages + +/** + * @function message_GetName + * @abstract The name in the message + * @discussion + * The name of the Interest or Content Object. If the caller will store the + * name, he should make a reference counted copy. + * @return The name as stored in the message object. + */ + +Name *message_GetName(const Message *message); + +/** + * Determines if the message has an Interest Lifetime parameter + * + * @param [in] message An allocated and parsed Message + * + * @retval true If an Intrerest Lifetime field exists + * @retval false If no Interest Lifetime exists + */ + +bool message_HasInterestLifetime(const Message *message); + +/** + * Returns the Interest lifetime in hicn-light Ticks + * + * the interest expires after now + returned ticks + * + * @param [in] message An allocated and parsed Message + * + * @retval integer Lifetime in forwarder Ticks + * + */ + +uint64_t message_GetInterestLifetimeTicks(const Message *message); + +/** + * checks if the expiry time is set inside the content object + */ +bool message_HasContentExpiryTime(const Message *message); + +/** + * returns the moment (in hicn-light ticks) when the content object will expire + */ +uint64_t message_GetContentExpiryTimeTicks(const Message *message); + +/** + * Returns a pointer to the beginning of the FixedHeader + * + * @param [in] message An allocated and parsed Message + * + * @return non-null The fixed header memory + * @return null No fixed header or an error + */ + +const uint8_t *message_FixedHeader(const Message *message); + +#endif // message_h diff --git a/hicn-light/src/core/messageHandler.h b/hicn-light/src/core/messageHandler.h new file mode 100755 index 000000000..d63656461 --- /dev/null +++ b/hicn-light/src/core/messageHandler.h @@ -0,0 +1,580 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef messageHandler +#define messageHandler + +#include + +#include +#include + +#define H(packet) ((hicn_header_t *)packet) +#define H6(packet) (H(packet)->v6.ip) +#define H6T(packet) (H(packet)->v6.tcp) +#define H4(packet) (H(packet)->v4.ip) +#define H4T(packet) (H(packet)->v4.tcp) + +#define HICN_V6_LEN(packet) (H6(packet).len) +#define HICN_V4_LEN(packet) (H4(packet).len) + +/*** codes and types ***/ +#define IPv6_TYPE 6 +#define IPv4_TYPE 4 +#define ICMP_WLDR_TYPE 42 +#define ICMP_WLDR_CODE 0 +#define ICMP_LB_TYPE 43 + +/*** masks and constants ***/ +#define PATH_LABEL_MASK 0x8000 // 1000 0000 0000 0000 +#define NOT_PATH_LABEL_MASK 0x7fff // 0111 0000 0000 0000 +#define UINT16_T_MASK 0x0000ffff // 1111 1111 1111 1111 +#define NEVER_EXPIRE \ + 16777216 // 2^16 (max urgent pointer) * 2^8 (max reserved + NS bits) + +/*** HICN ALLOWED PORTS ***/ +#define CONTROL_PORT 9695 +#define HTTP_PORT 8080 + +#define IPV6_DEFAULT_VERSION 6 +#define IPV6_DEFAULT_TRAFFIC_CLASS 0 +#define IPV6_DEFAULT_FLOW_LABEL 0 + +#define expected_lbl wldr_notification_lbl.expected_lbl +#define received_lbl wldr_notification_lbl.received_lbl + +static inline uint8_t messageHandler_GetIPPacketType(const uint8_t *message) { + return HICN_IP_VERSION(message); +} + +static inline void messageHandler_UpdateTCPCheckSum(uint8_t *message, + uint16_t *old_val, + uint16_t *new_val, + uint8_t size) { + switch (messageHandler_GetIPPacketType(message)) { + case IPv4_TYPE: + for (uint8_t i = 0; i < size; i++) { + uint16_t old_csum = ~(H4T(message).csum); + uint16_t not_old_val = ~(*old_val); + uint32_t sum = (uint32_t)old_csum + not_old_val + *new_val; + + while (sum >> 16) { + sum = (sum >> 16) + (sum & UINT16_T_MASK); + } + + H4T(message).csum = ~sum; + ++old_val; + ++new_val; + } + break; + case IPv6_TYPE: + for (uint8_t i = 0; i < size; i++) { + uint16_t old_csum = ~(H6T(message).csum); + uint16_t not_old_val = ~(*old_val); + uint32_t sum = (uint32_t)old_csum + not_old_val + *new_val; + + while (sum >> 16) { + sum = (sum >> 16) + (sum & UINT16_T_MASK); + } + + H6T(message).csum = ~sum; + ++old_val; + ++new_val; + } + break; + default: + return; + } +} + +static inline void messageHandler_UpdateIPv4CheckSum(uint8_t *message, + uint16_t *old_val, + uint16_t *new_val, + uint8_t size) { + for (uint8_t i = 0; i < size; i++) { + uint16_t old_csum = ~(H4(message).csum); + uint16_t not_old_val = ~(*old_val); + uint32_t sum = (uint32_t)old_csum + not_old_val + *new_val; + + while (sum >> 16) { + sum = (sum >> 16) + (sum & UINT16_T_MASK); + } + + H4(message).csum = ~sum; + ++old_val; + ++new_val; + } +} + +static inline size_t messageHandler_GetEmptyTCPPacketSize(unsigned ipVersion) { + if (ipVersion == IPv4_TYPE) + return IPV4_HDRLEN + TCP_HDRLEN; + else if (ipVersion == IPv6_TYPE) + return IPV6_HDRLEN + TCP_HDRLEN; + else + return 0; +} + +static inline size_t messageHandler_GetICMPPacketSize(unsigned ipVersion) { + if (ipVersion == IPv4_TYPE) + return IPV4_HDRLEN + ICMP_HDRLEN; + else if (ipVersion == IPv6_TYPE) + return IPV6_HDRLEN + ICMP_HDRLEN; + else + return 0; +} + +static inline size_t messageHandler_GetIPHeaderLength(unsigned ipVersion) { + if (ipVersion == IPv4_TYPE) + return IPV4_HDRLEN; + else if (ipVersion == IPv6_TYPE) + return IPV6_HDRLEN; + else + return 0; +} + +static inline bool messageHandler_IsValidHIcnPacket(const uint8_t *message) { + uint8_t version = messageHandler_GetIPPacketType(message); + if (version == IPv6_TYPE || version == IPv4_TYPE) { + return true; + } + return false; +} + +static inline uint8_t messageHandler_NextHeaderType(const uint8_t *message) { + switch (messageHandler_GetIPPacketType(message)) { + case IPv6_TYPE: + return (uint8_t)H6(message).nxt; + case IPv4_TYPE: + return (uint8_t)H4(message).protocol; + default: + return 0; + } +} + +static inline bool messageHandler_IsTCP(const uint8_t *message) { + if (messageHandler_NextHeaderType(message) != IPPROTO_TCP) return false; + return true; +} + +static inline bool messageHandler_IsInterest(const uint8_t *message) { + if (!messageHandler_IsTCP(message)) return false; + + bool flag; + hicn_packet_test_ece((hicn_header_t *)message, + &flag); // ECE flag is set to 0 in interest packets + if (flag == false) return true; + return false; +} + +static inline bool messageHandler_IsData(const uint8_t *message) { + if (!messageHandler_IsTCP(message)) return false; + + bool flag; + hicn_packet_test_ece((hicn_header_t *)message, + &flag); // ECE flag is set to 1 in data packets + if (flag == true) return true; + return false; +} + +static inline bool messageHandler_IsWldrNotification(const uint8_t *message) { + // this function returns true only if the packet is an ICMP packet in Wldr + // form. type must be equal to ICMP_WLDR_TYPE and code equal to ICMP_WLDR_CODE + uint8_t next_header = messageHandler_NextHeaderType(message); + + const uint8_t *icmp_ptr; + if (next_header == IPPROTO_ICMP) { + icmp_ptr = message + IPV4_HDRLEN; + } else if (next_header == IPPROTO_ICMPV6) { + icmp_ptr = message + IPV6_HDRLEN; + } else { + return false; + } + + uint8_t type = ((_icmp_header_t *)icmp_ptr)->type; + uint8_t code = ((_icmp_header_t *)icmp_ptr)->code; + if (type == ICMP_WLDR_TYPE && code == ICMP_WLDR_CODE) { + return true; + } + + return false; +} + +static inline bool messageHandler_IsLoadBalancerProbe(const uint8_t *message) { + uint8_t next_header = messageHandler_NextHeaderType(message); + + const uint8_t *icmp_ptr; + if (next_header == IPPROTO_ICMP) { + icmp_ptr = message + IPV4_HDRLEN; + } else if (next_header == IPPROTO_ICMPV6) { + icmp_ptr = message + IPV6_HDRLEN; + } else { + return false; + } + + uint8_t type = ((_icmp_header_t *)icmp_ptr)->type; + if (type == ICMP_LB_TYPE) { + return true; + } + + return false; +} + +static inline uint16_t messageHandler_GetTotalPacketLength( + const uint8_t *message) { + switch (messageHandler_GetIPPacketType(message)) { + case IPv6_TYPE: + return ntohs((uint16_t)HICN_V6_LEN(message)) + IPV6_HDRLEN; + case IPv4_TYPE: + return ntohs((uint16_t)HICN_V4_LEN(message)); + default: + return 0; + } +} + +static inline uint32_t messageHandler_GetSegment(const uint8_t *message) { + if (!messageHandler_IsTCP(message)) return 0; + + switch (messageHandler_GetIPPacketType(message)) { + case IPv6_TYPE: + return ntohl((uint32_t)H6T(message).seq); + case IPv4_TYPE: + return ntohl((uint32_t)H4T(message).seq); + default: + return 0; + } +} + +static inline uint16_t messageHandler_GetExpectedWldrLabel( + const uint8_t *message) { + const uint8_t *icmp_ptr; + switch (messageHandler_GetIPPacketType(message)) { + case IPv6_TYPE: + icmp_ptr = message + IPV6_HDRLEN; + break; + case IPv4_TYPE: + icmp_ptr = message + IPV4_HDRLEN; + break; + default: + return 0; + } + + return ntohs(((_icmp_wldr_header_t *)icmp_ptr)->expected_lbl); +} + +static inline uint16_t messageHandler_GetWldrLastReceived( + const uint8_t *message) { + const uint8_t *icmp_ptr; + switch (messageHandler_GetIPPacketType(message)) { + case IPv6_TYPE: + icmp_ptr = message + IPV6_HDRLEN; + break; + case IPv4_TYPE: + icmp_ptr = message + IPV4_HDRLEN; + break; + default: + return 0; + } + + return ntohs(((_icmp_wldr_header_t *)icmp_ptr)->received_lbl); +} + +static inline uint16_t messageHandler_GetWldrLabel(const uint8_t *message) { + switch (messageHandler_GetIPPacketType(message)) { + case IPv6_TYPE: + return ntohs((uint16_t)H6T(message).window); + case IPv4_TYPE: + return ntohs((uint16_t)H4T(message).window); + default: + return 0; + } +} + +static inline void messageHandler_SetWldrLabel(uint8_t *message, + uint16_t label) { + uint16_t old_val = messageHandler_GetWldrLabel(message); + + switch (messageHandler_GetIPPacketType(message)) { + case IPv6_TYPE: + H6T(message).window = htons(label); + break; + case IPv4_TYPE: + H4T(message).window = htons(label); + break; + default: + break; + } + + messageHandler_UpdateTCPCheckSum(message, &old_val, &label, 1); +} + +static inline void messageHandler_ResetWldrLabel(uint8_t *message) { + messageHandler_SetWldrLabel(message, 0); +} + +static inline bool messageHandler_HasWldr(const uint8_t *message) { + if (messageHandler_IsTCP(message)) { + uint16_t lbl = messageHandler_GetWldrLabel(message); + if (lbl != 0) { + return true; + } + } + return false; +} + +static inline uint8_t messageHandler_GetProbePacketType( + const uint8_t *message) { + const uint8_t *icmp_ptr; + switch (messageHandler_GetIPPacketType(message)) { + case IPv6_TYPE: + icmp_ptr = message + IPV6_HDRLEN; + break; + case IPv4_TYPE: + icmp_ptr = message + IPV4_HDRLEN; + break; + default: + return 0; + } + + return ((_icmp_header_t *)icmp_ptr)->code; +} + +static inline uint32_t messageHandler_GetPathLabel(const uint8_t *message) { + if (!messageHandler_IsTCP(message)) return 0; + + uint32_t path_label; + int res = hicn_data_get_path_label((hicn_header_t *)message, &path_label); + if (res < 0) return 0; + return path_label; +} + +static inline void messageHandler_SetPathLabel(uint8_t *message, + uint32_t new_path_label) { + if (!messageHandler_IsTCP(message)) return; + + uint32_t old_path_label; + int res = hicn_data_get_path_label((hicn_header_t *)message, &old_path_label); + if (res < 0) return; + + hicn_data_set_path_label((hicn_header_t *)message, new_path_label); + + messageHandler_UpdateTCPCheckSum(message, (uint16_t *)&old_path_label, + (uint16_t *)&new_path_label, 2); +} + +static inline void messageHandler_UpdatePathLabel(uint8_t *message, + uint8_t outFace) { + if (!messageHandler_IsTCP(message)) return; + + uint32_t pl_old_32bit = messageHandler_GetPathLabel(message); + uint8_t pl_old_8bit = (uint8_t)(pl_old_32bit >> 24UL); + uint32_t pl_new_32bit = + (uint32_t)((((pl_old_8bit << 1) | (pl_old_8bit >> 7)) ^ outFace) << 24UL); + + hicn_data_set_path_label((hicn_header_t *)message, pl_new_32bit); + + messageHandler_UpdateTCPCheckSum(message, (uint16_t *)&pl_old_32bit, + (uint16_t *)&pl_new_32bit, 2); +} + +static inline void messageHandler_ResetPathLabel(uint8_t *message) { + if (!messageHandler_IsTCP(message)) return; + + uint32_t pl_old_32bit = messageHandler_GetPathLabel(message); + uint32_t pl_new_32bit = 0; + hicn_data_set_path_label((hicn_header_t *)message, pl_new_32bit); + messageHandler_UpdateTCPCheckSum(message, (uint16_t *)&pl_old_32bit, + (uint16_t *)&pl_new_32bit, 2); +} + +static inline uint16_t messageHandler_GetInterestLifetime( + const uint8_t *message) { + if (!messageHandler_IsTCP(message)) return 0; + + hicn_lifetime_t lifetime; + int res = hicn_interest_get_lifetime((hicn_header_t *)message, &lifetime); + if (res < 0) return 0; + return lifetime; +} + +static inline bool messageHandler_HasInterestLifetime(const uint8_t *message) { + if (!messageHandler_IsTCP(message)) return false; + + if (messageHandler_GetInterestLifetime(message) == 0) return false; + return true; +} + +static inline uint32_t messageHandler_GetContentExpiryTime( + const uint8_t *message) { + if (!messageHandler_IsTCP(message)) return 0; + + uint32_t expirationTime; + int res = + hicn_data_get_expiry_time((hicn_header_t *)message, &expirationTime); + if (res < 0) return 0; + return expirationTime; +} + +static inline bool messageHandler_HasContentExpiryTime(const uint8_t *message) { + if (!messageHandler_IsTCP(message)) return 0; + + uint32_t expirationTime; + int res = + hicn_data_get_expiry_time((hicn_header_t *)message, &expirationTime); + if (res < 0) return false; + + if (expirationTime == NEVER_EXPIRE) return false; + + return true; +} + +static inline void *messageHandler_GetSource(const uint8_t *message) { + switch (messageHandler_GetIPPacketType(message)) { + case IPv6_TYPE: + return &H6(message).saddr; + break; + case IPv4_TYPE: + return &H4(message).saddr; + break; + default: + return NULL; + } +} + +static inline void *messageHandler_GetDestination(const uint8_t *message) { + switch (messageHandler_GetIPPacketType(message)) { + case IPv6_TYPE: + return &H6(message).daddr; + break; + case IPv4_TYPE: + return &H4(message).daddr; + break; + default: + return NULL; + } +} + +static inline void messageHandler_SetSource_IPv6(uint8_t *message, + struct in6_addr *address) { + if (messageHandler_IsTCP(message)) { + uint16_t *old_src = (uint16_t *)messageHandler_GetSource(message); + messageHandler_UpdateTCPCheckSum(message, old_src, (uint16_t *)address, 8); + } + H6(message).saddr.as_in6addr = *address; +} + +static inline void messageHandler_SetDestination_IPv6( + uint8_t *message, struct in6_addr *address) { + if (messageHandler_IsTCP(message)) { + uint16_t *old_dst = (uint16_t *)messageHandler_GetDestination(message); + messageHandler_UpdateTCPCheckSum(message, old_dst, (uint16_t *)address, 8); + } + H6(message).daddr.as_in6addr = *address; +} + +static inline void messageHandler_SetSource_IPv4(uint8_t *message, + uint32_t *address) { + // update tcp checksum + uint16_t *old_src = (uint16_t *)messageHandler_GetSource(message); + if (messageHandler_IsTCP(message)) { + messageHandler_UpdateTCPCheckSum(message, old_src, (uint16_t *)address, 2); + } + // update IPv4 cheksum + // the IPv4 checksum is not part of the psudo header for TCP checksum + // calculation we can update them separetelly + messageHandler_UpdateIPv4CheckSum(message, old_src, (uint16_t *)address, 2); + + H4(message).saddr.as_u32 = *address; +} + +static inline void messageHandler_SetDestination_IPv4(uint8_t *message, + uint32_t *address) { + uint16_t *old_dst = (uint16_t *)messageHandler_GetDestination(message); + if (messageHandler_IsTCP(message)) { + messageHandler_UpdateTCPCheckSum(message, old_dst, (uint16_t *)address, 2); + } + messageHandler_UpdateIPv4CheckSum(message, old_dst, (uint16_t *)address, 2); + H4(message).daddr.as_u32 = *address; +} + +static inline void messageHandler_SetWldrNotification(uint8_t *notification, + uint8_t *original, + uint16_t expected, + uint16_t received) { + hicn_header_t *h = (hicn_header_t *)notification; + switch (messageHandler_GetIPPacketType(original)) { + case IPv6_TYPE: { + *h = (hicn_header_t){.v6 = { + .ip = + { + .version_class_flow = htonl( + (IPV6_DEFAULT_VERSION << 28) | + (IPV6_DEFAULT_TRAFFIC_CLASS << 20) | + (IPV6_DEFAULT_FLOW_LABEL & 0xfffff)), + .len = htons(ICMP_HDRLEN), + .nxt = IPPROTO_ICMPV6, + .hlim = 5, + }, + .wldr = + { + .type = ICMP_WLDR_TYPE, + .code = ICMP_WLDR_CODE, + .expected_lbl = htons(expected), + .received_lbl = htons(received), + }, + }}; + messageHandler_SetSource_IPv6( + notification, + (struct in6_addr *)messageHandler_GetDestination(original)); + messageHandler_SetDestination_IPv6( + notification, (struct in6_addr *)messageHandler_GetSource(original)); + break; + } + case IPv4_TYPE: { + break; + } + default: + break; + } +} + +static inline void messageHandler_SetProbePacket(uint8_t *message, + uint8_t probeType, + struct in6_addr *src, + struct in6_addr *dst) { + hicn_header_t *h = (hicn_header_t *)message; + *h = (hicn_header_t){ + .v6 = { + .ip = + { + .version_class_flow = + htonl((IPV6_DEFAULT_VERSION << 28) | + (IPV6_DEFAULT_TRAFFIC_CLASS << 20) | + (IPV6_DEFAULT_FLOW_LABEL & 0xfffff)), + .len = htons(ICMP_HDRLEN), + .nxt = IPPROTO_ICMPV6, + .hlim = 5, // this should be 1, but ... just to be safe + }, + .icmp = + { + .type = ICMP_LB_TYPE, + .code = probeType, + }, + }}; + messageHandler_SetSource_IPv6(message, src); + messageHandler_SetDestination_IPv6(message, dst); +} + +#endif // Metis_metis_MessageHandler diff --git a/hicn-light/src/core/messagePacketType.h b/hicn-light/src/core/messagePacketType.h new file mode 100755 index 000000000..dfbb12342 --- /dev/null +++ b/hicn-light/src/core/messagePacketType.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file message_packet_type_h + * @brief Defines the packet type for a HICN message + * + */ + +#ifndef message_packet_type_h +#define message_packet_type_h + +typedef enum message_type { + MessagePacketType_Unknown, + MessagePacketType_Interest, + MessagePacketType_ContentObject, + MessagePacketType_WldrNotification +} MessagePacketType; + +#endif // message_packet_type_h diff --git a/hicn-light/src/core/name.c b/hicn-light/src/core/name.c new file mode 100755 index 000000000..f6a452d27 --- /dev/null +++ b/hicn-light/src/core/name.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#include + +#define IPv6_TYPE 6 +#define IPv4_TYPE 4 + +// assumption: the IPv6 address is the name, the TCP segment number is the ICN +// segment + +struct name { + NameBitvector *content_name; + uint32_t segment; + uint32_t name_hash; + // the refcount is shared between all copies + unsigned *refCountPtr; +}; + +// ===================================================== + +static unsigned _getRefCount(const Name *name) { return *name->refCountPtr; } + +static void _incrementRefCount(Name *name) { + parcAssertTrue(*name->refCountPtr > 0, + "Illegal State: Trying to increment a 0 refcount!"); + (*name->refCountPtr)++; +} + +static void _decrementRefCount(Name *name) { + parcAssertTrue(*name->refCountPtr > 0, + "Illegal State: Trying to decrement a 0 refcount!"); + (*name->refCountPtr)--; +} + +static uint32_t _computeHash(Name *name) { + parcAssertNotNull(name, "Parameter must be non-null pointer"); + + uint32_t hash1 = nameBitvector_GetHash32(name->content_name); + return parcHash32_Data_Cumulative((const uint8_t *)&name->segment, 4, hash1); +} + +// ============================================================================ + +Name *name_CreateFromPacket(const uint8_t *packet, MessagePacketType type) { + Name *name = parcMemory_AllocateAndClear(sizeof(Name)); + parcAssertNotNull(name, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Name)); + + if (messageHandler_GetIPPacketType(packet) == IPv6_TYPE) { + if (type == MessagePacketType_Interest) { + name->content_name = nameBitvector_CreateFromIn6Addr( + (struct in6_addr *)messageHandler_GetDestination(packet), 128); + } else if (type == MessagePacketType_ContentObject) { + name->content_name = nameBitvector_CreateFromIn6Addr( + (struct in6_addr *)messageHandler_GetSource(packet), 128); + } else { + parcMemory_Deallocate((void **)&name); + return NULL; + } + } else if (messageHandler_GetIPPacketType(packet) == IPv4_TYPE) { + if (type == MessagePacketType_Interest) { + name->content_name = nameBitvector_CreateFromInAddr( + *((uint32_t *)messageHandler_GetDestination(packet)), 32); + } else if (type == MessagePacketType_ContentObject) { + name->content_name = nameBitvector_CreateFromInAddr( + *((uint32_t *)messageHandler_GetSource(packet)), 32); + } else { + parcMemory_Deallocate((void **)&name); + return NULL; + } + } else { + printf("Error: unknown message type\n"); + parcMemory_Deallocate((void **)&name); + return NULL; + } + + name->segment = messageHandler_GetSegment(packet); + name->name_hash = _computeHash(name); + + name->refCountPtr = parcMemory_Allocate(sizeof(unsigned)); + parcAssertNotNull(name->refCountPtr, "parcMemory_Allocate(%zu) returned NULL", + sizeof(unsigned)); + *name->refCountPtr = 1; + return name; +} + +Name *name_CreateFromAddress(address_type addressType, union commandAddr addr, + uint8_t len) { + Name *name = parcMemory_AllocateAndClear(sizeof(Name)); + parcAssertNotNull(name, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Name)); + if (addressType == ADDR_INET) { + name->content_name = nameBitvector_CreateFromInAddr(addr.ipv4, len); + } else if (addressType == ADDR_INET6) { + name->content_name = nameBitvector_CreateFromIn6Addr(&addr.ipv6, len); + } else { + parcTrapNotImplemented("Unkown packet type"); + } + + name->segment = 0; + name->name_hash = _computeHash(name); + + name->refCountPtr = parcMemory_Allocate(sizeof(unsigned)); + parcAssertNotNull(name->refCountPtr, "parcMemory_Allocate(%zu) returned NULL", + sizeof(unsigned)); + *name->refCountPtr = 1; + + return name; +} + +void name_Release(Name **namePtr) { + parcAssertNotNull(namePtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*namePtr, "Parameter must dereference to non-null pointer"); + + Name *name = *namePtr; + _decrementRefCount(name); + if (_getRefCount(name) == 0) { + parcMemory_Deallocate((void **)&(name->refCountPtr)); + nameBitvector_Destroy(&(name->content_name)); + } + parcMemory_Deallocate((void **)&name); + *namePtr = NULL; +} + +Name *name_Acquire(const Name *original) { + parcAssertNotNull(original, "Parameter must be non-null"); + Name *copy = parcMemory_AllocateAndClear(sizeof(Name)); + parcAssertNotNull(copy, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Name)); + + memcpy(copy, original, sizeof(Name)); + _incrementRefCount(copy); + + return copy; +} + +uint32_t name_HashCode(const Name *name) { + parcAssertNotNull(name, "Parameter must be non-null"); + return name->name_hash; +} + +NameBitvector *name_GetContentName(const Name *name) { + parcAssertNotNull(name, "Parameter must be non-null"); + return name->content_name; +} + +bool name_Equals(const Name *a, const Name *b) { + parcAssertNotNull(a, "Parameter a must be non-null"); + parcAssertNotNull(b, "Parameter b must be non-null"); + + if ((nameBitvector_Equals(a->content_name, b->content_name) && + a->segment == b->segment)) + return true; + return false; +} + +int name_Compare(const Name *a, const Name *b) { + parcAssertNotNull(a, "Parameter a must be non-null"); + parcAssertNotNull(b, "Parameter b must be non-null"); + + if (a == NULL && b == NULL) { + return 0; + } + if (a == NULL) { + return -1; + } + if (b == NULL) { + return +1; + } + + int res = nameBitvector_Compare(a->content_name, b->content_name); + + if (res != 0) { + return res; + } else { + if (a->segment < b->segment) { + return -1; + } else if (a->segment > b->segment) { + return +1; + } else { + return 0; + } + } +} + +bool name_StartsWith(const Name *name, const Name *prefix) { + parcAssertNotNull(name, "Parameter name must be non-null"); + parcAssertNotNull(prefix, "Parameter prefix must be non-null"); + + return nameBitvector_StartsWith(name->content_name, prefix->content_name); +} + +char *name_ToString(const Name *name) { + char *output = malloc(128); + + Address *packetAddr = nameBitvector_ToAddress(name_GetContentName(name)); + + sprintf(output, "name: %s seq: %u", addressToString(packetAddr), + name->segment); + + addressDestroy(&packetAddr); + + return output; +} + +void name_setLen(const Name *name, uint8_t len) { + nameBitvector_setLen(name->content_name, len); +} diff --git a/hicn-light/src/core/name.h b/hicn-light/src/core/name.h new file mode 100755 index 000000000..fb4ad7a56 --- /dev/null +++ b/hicn-light/src/core/name.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef name_h +#define name_h + +#include +#include + +#include +#include +#include + +#include + +struct name; +typedef struct name Name; + +/** + * Creates a name from packet + * + */ +Name *name_CreateFromPacket(const uint8_t *memory, MessagePacketType type); + +/** + * Releases one reference count, and frees memory after last reference + */ +void name_Release(Name **namePtr); + +/** + * Acquires a reference to the name so that a reference count increments. + * Notice however that this * function is used only when a new fib entry is + * created (mostly configuration time) probably here performance are not + * critical. + */ +Name *name_Acquire(const Name *original); + +/** + * A hash value for use in hash tables + * + */ +uint32_t name_HashCode(const Name *name); + +/** + * Returns the content name without the segment value + * + */ +NameBitvector *name_GetContentName(const Name *name); + +/** + * Determine if two HicnName instances are equal. + */ +bool name_Equals(const Name *a, const Name *b); + +/** + * Compares two names and returns their ordering + * + */ +int name_Compare(const Name *a, const Name *b); + +/** + * @function metsName_StartsWith + * @abstract Checks if name starts with prefix + * @discussion + * Byte-by-byte prefix comparison + * + * @return True if the name is equal to or begins with prefix + */ + +bool name_StartsWith(const Name *name, const Name *prefix); + +/** + * return the name in string format (bitvector + segment number) + * + */ +char *name_ToString(const Name *name); + +/** + * @function message_setNameLen + * @abstract Sets a message name length + * @param [in] message - Interest message + * @param [in] len - Name length + */ +void name_setLen(const Name *name, uint8_t len); + +/** + * Creates a name from a Address + * + */ +Name *name_CreateFromAddress(address_type addressType, union commandAddr addr, + uint8_t len); + +#endif // name_h diff --git a/hicn-light/src/core/nameBitvector.c b/hicn-light/src/core/nameBitvector.c new file mode 100755 index 000000000..66f3eae20 --- /dev/null +++ b/hicn-light/src/core/nameBitvector.c @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include + +#define BLOCKS 2 + +const uint64_t BLOCK_SIZE = 64; +const uint64_t WIDTH = 128; +const uint64_t BLOCK_ONE = 0x1; + +// the bits are encoded in the following order: +// 00100101001---101010 00100011---110100100 +// [bits[0] (uint64_t)] [bits[1] (uint64_t)] +// ^ ^ ^ ^ +// 0 63 64 127 +// address 2200::0011 is encoded as: +// 1000 1000 0000 0010 00000 ....0100 0100 +// ^ ^ +// 0 127 + +struct name_bitvector { + uint64_t bits[BLOCKS]; + uint8_t len; + uint8_t IPversion; +}; + +NameBitvector *nameBitvector_CreateFromInAddr(uint32_t s_addr, uint8_t len) { + NameBitvector *bitvector = parcMemory_AllocateAndClear(sizeof(NameBitvector)); + parcAssertNotNull(bitvector, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(NameBitvector)); + + bitvector->bits[0] = 0; + bitvector->bits[1] = 0; + + uint8_t addr_1 = (s_addr & 0xff000000) >> 24; + uint8_t addr_2 = (s_addr & 0x00ff0000) >> 16; + uint8_t addr_3 = (s_addr & 0x0000ff00) >> 8; + uint8_t addr_4 = (s_addr & 0x000000ff); + + bitvector->bits[1] = (bitvector->bits[1] | addr_4) << 8; + bitvector->bits[1] = (bitvector->bits[1] | addr_3) << 8; + bitvector->bits[1] = (bitvector->bits[1] | addr_2) << 8; + bitvector->bits[1] = (bitvector->bits[1] | addr_1); + bitvector->bits[1] = bitvector->bits[1] << 32; + + bitvector->len = len; + + bitvector->IPversion = IPv4_TYPE; + + return bitvector; +} + +NameBitvector *nameBitvector_CreateFromIn6Addr(struct in6_addr *addr, + uint8_t len) { + parcAssertNotNull(addr, "addr cannot be null"); + + NameBitvector *bitvector = parcMemory_AllocateAndClear(sizeof(NameBitvector)); + parcAssertNotNull(bitvector, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(NameBitvector)); + + bitvector->bits[0] = 0; + bitvector->bits[1] = 0; + + for (int i = 0; i < 8; ++i) { + bitvector->bits[1] = (bitvector->bits[1] << 8) | addr->s6_addr[i]; + } + + for (int i = 8; i < 16; ++i) { + bitvector->bits[0] = (bitvector->bits[0] << 8) | addr->s6_addr[i]; + } + + bitvector->len = len; + + bitvector->IPversion = IPv6_TYPE; + + return bitvector; +} + +NameBitvector *nameBitvector_CreateFromAddress(const Address *prefix, + uint8_t len) { + parcAssertNotNull(prefix, "prefix cannot be null"); + + NameBitvector *bitvector = NULL; + switch (addressGetType(prefix)) { + case ADDR_INET: { + struct sockaddr_in addr; + addressGetInet(prefix, &addr); + bitvector = nameBitvector_CreateFromInAddr(addr.sin_addr.s_addr, len); + break; + } + case ADDR_INET6: { + struct sockaddr_in6 addr; + addressGetInet6(prefix, &addr); + bitvector = nameBitvector_CreateFromIn6Addr(&addr.sin6_addr, len); + break; + } + default: + parcTrapNotImplemented("Unkown packet type"); + break; + } + + return bitvector; +} + +NameBitvector *nameBitvector_Copy(const NameBitvector *original) { + parcAssertNotNull(original, "original cannot be null"); + + NameBitvector *copy = parcMemory_AllocateAndClear(sizeof(NameBitvector)); + parcAssertNotNull(copy, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(NameBitvector)); + + copy->bits[0] = original->bits[0]; + copy->bits[1] = original->bits[1]; + copy->len = original->len; + + return copy; +} + +void nameBitvector_Destroy(NameBitvector **bitvectorPtr) { + parcAssertNotNull(bitvectorPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*bitvectorPtr, + "Parameter must dereference to non-null pointer"); + + NameBitvector *bv = *bitvectorPtr; + parcMemory_Deallocate((void **)&(bv)); + *bitvectorPtr = NULL; +} + +uint8_t nameBitvector_GetLength(const NameBitvector *name) { return name->len; } + +uint32_t nameBitvector_GetHash32(const NameBitvector *name) { + return parcHash32_Data_Cumulative((const uint8_t *)name->bits, 16, 0); +} + +bool nameBitvector_Equals(const NameBitvector *a, const NameBitvector *b) { + if (a->bits[0] == b->bits[0] && a->bits[1] == b->bits[1] && a->len == b->len) + return true; + return false; +} + +int nameBitvector_Compare(const NameBitvector *a, const NameBitvector *b) { + if (a == NULL && b == NULL) { + return 0; + } + if (a == NULL) { + return -1; + } + if (b == NULL) { + return +1; + } + + if (a->bits[0] < b->bits[0]) { + return -1; + } else if (a->bits[0] > b->bits[0]) { + return +1; + } else if (a->bits[1] < b->bits[1]) { + return -1; + } else if (a->bits[1] > b->bits[1]) { + return +1; + } else if (a->len < b->len) { + return -1; + } else if (a->len > b->len) { + return +1; + } else { + return 0; + } +} + +bool nameBitvector_StartsWith(const NameBitvector *name, + const NameBitvector *prefix) { + parcAssertNotNull(name, "name cannot be NULL"); + parcAssertNotNull(prefix, "prefix cannot be NULL"); + parcAssertTrue(prefix->len > 0, "prefix length can not be 0"); + + if (prefix->len > BLOCK_SIZE) + return (name->bits[1] == prefix->bits[1]) && + ((name->bits[0] ^ prefix->bits[0]) >> + (BLOCK_SIZE - (prefix->len - BLOCK_SIZE)) == + 0); + + return ((name->bits[1] ^ prefix->bits[1]) >> (BLOCK_SIZE - prefix->len) == 0); +} + +bool nameBitvector_testBit(const NameBitvector *name, uint8_t pos) { + if (pos == WIDTH) pos = 127; + + uint8_t final_pos = WIDTH - name->len; + + // the bit to test is inside the name/prefix len + if (pos > final_pos) { + return (name->bits[pos / BLOCK_SIZE] & (BLOCK_ONE << (pos % BLOCK_SIZE))); + } + + // the bit to test is outside the name/prefix len + if (pos < final_pos) { + return false; + } + + // pos is equal to the name/prefix len + return true; +} + +uint64_t _diff_bit_log2(uint64_t val) { + // base 2 log of an uint64_t. This is the same as get the position of + // the highest bit set (or most significant bit set, MSB) + uint64_t result = 0; + + if (val & 0xFFFFFFFF00000000) { + val = val >> 32; + result = result | 32; + } + if (val & 0xFFFF0000) { + val = val >> 16; + result = result | 16; + } + if (val & 0xFF00) { + val = val >> 8; + result = result | 8; + } + if (val & 0xF0) { + val = val >> 4; + result = result | 4; + } + if (val & 0xC) { + val = val >> 2; + result = result | 2; + } + if (val & 0x2) { + val = val >> 1; + result = result | 1; + } + return result; +} + +uint8_t nameBitvector_firstDiff(const NameBitvector *a, + const NameBitvector *b) { + uint8_t res = 0; + uint64_t diff = a->bits[1] ^ b->bits[1]; + if (diff) + res = 64 + _diff_bit_log2(diff); + else + res = _diff_bit_log2(a->bits[0] ^ b->bits[0]); + + // res is computed over the bitvector which is composed by 128 bit all the + // times however the prefixes may be diffrent just because the have different + // lengths example: prefix 1: 0::/30 prefix 2: 0::/20 at this point of the + // function res would be 0 since both the bitvectors are composed by 0s but the + // function will return 127-20, which is the position at which the two prefix + // are different, since prefix 2 has only 20 bits + + uint8_t len_diff; + if (a->len < b->len) + len_diff = WIDTH - a->len; + else + len_diff = WIDTH - b->len; + + if (len_diff > res) res = len_diff; + + return res; +} + +int nameBitvector_ToIPAddress(const NameBitvector *name, + ip_address_t *ip_address) { + if (name->IPversion == IPv4_TYPE) { + struct in_addr *addr = (struct in_addr *)(&ip_address->buffer); + ip_address->family = AF_INET; + ip_address->prefix_len = IPV4_ADDR_LEN_BITS; + + uint32_t tmp_addr = name->bits[1] >> 32ULL; + uint8_t addr_1 = (tmp_addr & 0xff000000) >> 24; + uint8_t addr_2 = (tmp_addr & 0x00ff0000) >> 16; + uint8_t addr_3 = (tmp_addr & 0x0000ff00) >> 8; + uint8_t addr_4 = (tmp_addr & 0x000000ff); + + addr->s_addr = 0; + addr->s_addr = (addr->s_addr | addr_4) << 8; + addr->s_addr = (addr->s_addr | addr_3) << 8; + addr->s_addr = (addr->s_addr | addr_2) << 8; + addr->s_addr = (addr->s_addr | addr_1); + + } else { + struct in6_addr *addr = (struct in6_addr *)(&ip_address->buffer); + ip_address->family = AF_INET6; + ip_address->prefix_len = name->len; // IPV6_ADDR_LEN_BITS; + + for (int i = 0; i < 8; i++) { + addr->s6_addr[i] = (uint8_t)((name->bits[1] >> 8 * (7 - i)) & 0xFF); + } + + int x = 0; + for (int i = 8; i < 16; ++i) { + addr->s6_addr[i] = (uint8_t)((name->bits[0] >> 8 * (7 - x)) & 0xFF); + x++; + } + } + return true; +} + +void nameBitvector_setLen(NameBitvector *name, uint8_t len) { name->len = len; } + +Address *nameBitvector_ToAddress(const NameBitvector *name) { + if (name->IPversion == IPv4_TYPE) { + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(1234); + + uint32_t tmp_addr = name->bits[1] >> 32ULL; + uint8_t addr_1 = (tmp_addr & 0xff000000) >> 24; + uint8_t addr_2 = (tmp_addr & 0x00ff0000) >> 16; + uint8_t addr_3 = (tmp_addr & 0x0000ff00) >> 8; + uint8_t addr_4 = (tmp_addr & 0x000000ff); + + addr.sin_addr.s_addr = 0; + addr.sin_addr.s_addr = (addr.sin_addr.s_addr | addr_4) << 8; + addr.sin_addr.s_addr = (addr.sin_addr.s_addr | addr_3) << 8; + addr.sin_addr.s_addr = (addr.sin_addr.s_addr | addr_2) << 8; + addr.sin_addr.s_addr = (addr.sin_addr.s_addr | addr_1); + + Address *packetAddr = addressCreateFromInet(&addr); + + return packetAddr; + + } else { + struct sockaddr_in6 addr; + addr.sin6_family = AF_INET6; + addr.sin6_port = htons(1234); + addr.sin6_scope_id = 0; + addr.sin6_flowinfo = 0; + + for (int i = 0; i < 8; i++) { + addr.sin6_addr.s6_addr[i] = + (uint8_t)((name->bits[1] >> 8 * (7 - i)) & 0xFF); + } + + int x = 0; + for (int i = 8; i < 16; ++i) { + addr.sin6_addr.s6_addr[i] = + (uint8_t)((name->bits[0] >> 8 * (7 - x)) & 0xFF); + x++; + } + + Address *packetAddr = addressCreateFromInet6(&addr); + + return packetAddr; + } +} + +char *nameBitvector_ToString(const NameBitvector *name) { + char *output = malloc(WIDTH); + + Address *packetAddr = nameBitvector_ToAddress(name); + + sprintf(output, "prefix: %s len: %u", addressToString(packetAddr), name->len); + + addressDestroy(&packetAddr); + + return output; +} \ No newline at end of file diff --git a/hicn-light/src/core/nameBitvector.h b/hicn-light/src/core/nameBitvector.h new file mode 100755 index 000000000..28a31dc26 --- /dev/null +++ b/hicn-light/src/core/nameBitvector.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef name_bitvector_h +#define name_bitvector_h + +#include +#include +#include + +#include + +struct name_bitvector; +typedef struct name_bitvector NameBitvector; + +NameBitvector *nameBitvector_CreateFromInAddr(uint32_t s_addr, uint8_t len); + +NameBitvector *nameBitvector_CreateFromIn6Addr(struct in6_addr *addr, + uint8_t len); + +NameBitvector *nameBitvector_CreateFromAddress(const Address *prefix, + uint8_t len); + +NameBitvector *nameBitvector_Copy(const NameBitvector *original); + +void nameBitvector_Destroy(NameBitvector **bitvectorPtr); + +uint8_t nameBitvector_GetLength(const NameBitvector *name); + +uint32_t nameBitvector_GetHash32(const NameBitvector *name); + +bool nameBitvector_Equals(const NameBitvector *a, const NameBitvector *b); + +int nameBitvector_Compare(const NameBitvector *a, const NameBitvector *b); + +bool nameBitvector_StartsWith(const NameBitvector *name, + const NameBitvector *prefix); + +bool nameBitvector_testBit(const NameBitvector *name, uint8_t pos); + +uint8_t nameBitvector_firstDiff(const NameBitvector *a, const NameBitvector *b); + +int nameBitvector_ToIPAddress(const NameBitvector *name, + ip_address_t *ip_address); +void nameBitvector_setLen(NameBitvector *name, uint8_t len); + +Address *nameBitvector_ToAddress(const NameBitvector *name); + +char *nameBitvector_ToString(const NameBitvector *name); + +#endif // name_bitvector_h diff --git a/hicn-light/src/core/numberSet.c b/hicn-light/src/core/numberSet.c new file mode 100755 index 000000000..75fec1524 --- /dev/null +++ b/hicn-light/src/core/numberSet.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include + +struct number_set { + Number *arrayOfNumbers; + size_t length; + size_t limit; + unsigned refcount; +}; + +static void numberSet_Expand(NumberSet *set); + +NumberSet *numberSet_Create() { + NumberSet *set = parcMemory_AllocateAndClear(sizeof(NumberSet)); + parcAssertNotNull(set, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(NumberSet)); + set->arrayOfNumbers = parcMemory_AllocateAndClear(sizeof(Number) * 16); + parcAssertNotNull((set->arrayOfNumbers), + "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Number) * 16); + set->length = 0; + set->limit = 16; + set->refcount = 1; + return set; +} + +NumberSet *numberSet_Acquire(const NumberSet *original) { + parcAssertNotNull(original, "Parameter original must be non-null"); + NumberSet *copy = (NumberSet *)original; + copy->refcount++; + return copy; +} + +void numberSet_Release(NumberSet **setPtr) { + parcAssertNotNull(setPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*setPtr, "Parameter must dereference to non-null pointer"); + + NumberSet *set = *setPtr; + parcAssertTrue( + set->refcount > 0, + "Invalid state: calling destroy on an object with 0 reference count"); + set->refcount--; + + if (set->refcount == 0) { + parcMemory_Deallocate((void **)&(set->arrayOfNumbers)); + parcMemory_Deallocate((void **)&set); + *setPtr = NULL; + } +} + +/** + * @function numberSet_AddNoChecks + * @abstract Add a number we know is not already in the set + * @discussion + * Used by other functions that already know the number is unique in the set, + * Does not do the expensive Contains check. + */ +static void numberSet_AddNoChecks(NumberSet *set, Number number) { + if (set->length == set->limit) { + numberSet_Expand(set); + } + + set->arrayOfNumbers[set->length] = number; + set->length++; +} + +bool numberSet_Add(NumberSet *set, Number number) { + parcAssertNotNull(set, "Parameter set must be non-null"); + if (numberSet_Contains(set, number)) { + return false; + } + + numberSet_AddNoChecks(set, number); + return true; +} + +size_t numberSet_Length(const NumberSet *set) { + parcAssertNotNull(set, "Parameter set must be non-null"); + return set->length; +} + +Number numberSet_GetItem(const NumberSet *set, size_t ordinalIndex) { + parcAssertNotNull(set, "Parameter set must be non-null"); + parcAssertTrue(ordinalIndex < set->length, + "Limit beyond end of set, length %zu got %zu", set->length, + ordinalIndex); + + return set->arrayOfNumbers[ordinalIndex]; +} + +bool numberSet_Contains(const NumberSet *set, Number number) { + parcAssertNotNull(set, "Parameter set must be non-null"); + for (size_t i = 0; i < set->length; i++) { + if (set->arrayOfNumbers[i] == number) { + return true; + } + } + return false; +} + +void numberSet_AddSet(NumberSet *destinationSet, const NumberSet *setToAdd) { + parcAssertNotNull(destinationSet, + "Parameter destinationSet must be non-null"); + parcAssertNotNull(setToAdd, "Parameter setToAdd must be non-null"); + + for (size_t i = 0; i < setToAdd->length; i++) { + numberSet_Add(destinationSet, setToAdd->arrayOfNumbers[i]); + } +} + +NumberSet *numberSet_Subtract(const NumberSet *minuend, + const NumberSet *subtrahend) { + // because the underlying ADT is not sorted, this is pretty ineffient, could + // be O(n^2). + + NumberSet *difference = numberSet_Create(); + + for (size_t i = 0; i < minuend->length; i++) { + bool unique = true; + for (size_t j = 0; j < subtrahend->length && unique; j++) { + if (minuend->arrayOfNumbers[i] == subtrahend->arrayOfNumbers[j]) { + unique = false; + } + } + + if (unique) { + numberSet_AddNoChecks(difference, minuend->arrayOfNumbers[i]); + } + } + return difference; +} + +bool numberSet_Equals(const NumberSet *a, const NumberSet *b) { + if (a == NULL && b == NULL) { + return true; + } + + if (a == NULL || b == NULL) { + return false; + } + + if (a->length == b->length) { + for (size_t i = 0; i < a->length; i++) { + bool found = false; + for (size_t j = 0; j < b->length && !found; j++) { + if (a->arrayOfNumbers[i] == b->arrayOfNumbers[j]) { + found = true; + } + } + if (!found) { + return false; + } + } + return true; + } + + return false; +} + +void numberSet_Remove(NumberSet *set, Number number) { + parcAssertNotNull(set, "Parameter set must be non-null"); + for (size_t i = 0; i < set->length; i++) { + if (set->arrayOfNumbers[i] == number) { + set->length--; + if (set->length > 0) { + // move the last element to the removed element to keep the array + // packed. + set->arrayOfNumbers[i] = set->arrayOfNumbers[set->length]; + } + return; + } + } +} + +// ===================================================== + +static void numberSet_Expand(NumberSet *set) { + size_t newlimit = set->limit * 2; + size_t newbytes = newlimit * sizeof(Number); + + set->arrayOfNumbers = parcMemory_Reallocate(set->arrayOfNumbers, newbytes); + set->limit = newlimit; +} diff --git a/hicn-light/src/core/numberSet.h b/hicn-light/src/core/numberSet.h new file mode 100755 index 000000000..91a965d7b --- /dev/null +++ b/hicn-light/src/core/numberSet.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief Stores a set of numbers. + * + * Useful for things like the reverse path of a PIT + * or the forward paths of a FIB. Does not allow duplicates. + * + */ + +#ifndef numberSet_h +#define numberSet_h + +#include +#include +#include + +struct number_set; +typedef struct number_set NumberSet; + +typedef uint32_t Number; + +/** + * @function numberList_Create + * @abstract A new list of numbers + */ +NumberSet *numberSet_Create(void); + +/** + * Obtains a reference counted copy of the original + * The reference count is increased by one. It must be released with + * NumberSet_Release(). + * @param [in] original An allocated NumberSet + * @return non-null The reference counted copy + */ +NumberSet *numberSet_Acquire(const NumberSet *original); + +/** + * Releases one reference count and destroys the memory after last release + * The pointer will be NULLed after release regardless if the memory was + * destroyed. + * @param [in,out] setPtr A pointer to a NumberSet. Will be NULL'd after + * release. + */ +void numberSet_Release(NumberSet **setPtr); + +/** + * @function numberList_Append + * @abstract Add a number to the end of the list + * @discussion + * No check for duplicates is done + * @return true if added, false if a duplicate + */ +bool numberSet_Add(NumberSet *set, Number number); + +/** + * @function numberList_Length + * @abstract The count of numbers in the list + */ +size_t numberSet_Length(const NumberSet *set); + +/** + * @function numberSet_GetItem + * @abstract Retrieves an item based on the ordinal index + * @discussion + * Will assert if the ordinalIndex is out of bounds. + */ +Number numberSet_GetItem(const NumberSet *set, size_t ordinalIndex); + +/** + * @function numberSet_Contains + * @abstract Checks for set membership + * @return true if the set contains the number, false otherwise + */ +bool numberSet_Contains(const NumberSet *set, Number number); + +/** + * @function numberSet_AddSet + * @abstract Adds one set to another set + * @discussion + * Adds setToAdd to destinationSet + * @return true if the set contains the number, false otherwise + */ +void numberSet_AddSet(NumberSet *destinationSet, const NumberSet *setToAdd); + +/** + * @function numberSet_Subtract + * @abstract Computes set difference difference = minuend - + * subtrahend, returns a new number set. + * @discussion + * minuend and subtrahend are not modified. A new + * difference set is created. + * + * Returns the elements in minuend that are not in + * subtrahend. + * + * @param minuend The set from which to subtract + * @param subrahend The set begin removed from minuend + * @return The set difference. May be empty, but will not be NULL. + */ +NumberSet *numberSet_Subtract(const NumberSet *minuend, + const NumberSet *subtrahend); + +/** + * Determine if two NumberSet instances are equal. + * + * Two NumberSet instances are equal if, and only if, + * they are the same size and contain the same elements. Empty sets are + * equal. NULL equals NULL, but does not equal non-NULL. + * + * The following equivalence relations on non-null `NumberSet` instances are + * maintained: + * + * * It is reflexive: for any non-null reference value x, `NumberSet_Equals(x, + * x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `numberSet_Equals(x, y)` must return true if and only if + * `numberSet_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `numberSet_Equals(x, y)` returns true and + * `numberSet_Equals(y, z)` returns true, + * then `numberSet_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `numberSet_Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `numberSet_Equals(x, NULL)` must + * return false. + * + * @param a A pointer to a `NumberSet` instance. + * @param b A pointer to a `NumberSet` instance. + * @return true if the two `NumberSet` instances are equal. + */ +bool numberSet_Equals(const NumberSet *a, const NumberSet *b); + +/** + * @function numberSet_Remove + * @abstract Removes the number from the set + */ +void numberSet_Remove(NumberSet *set, Number number); +#endif // numberSet_h diff --git a/hicn-light/src/core/streamBuffer.c b/hicn-light/src/core/streamBuffer.c new file mode 100755 index 000000000..7aebb5edb --- /dev/null +++ b/hicn-light/src/core/streamBuffer.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include + +void streamBuffer_Destroy(PARCEventQueue **bufferPtr) { + parcAssertNotNull(bufferPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*bufferPtr, + "Parameter must dereference to non-null pointer"); + parcEventQueue_Destroy(bufferPtr); + *bufferPtr = NULL; +} + +void streamBuffer_SetWatermark(PARCEventQueue *buffer, bool setRead, + bool setWrite, size_t low, size_t high) { + parcAssertNotNull(buffer, "Parameter buffer must be non-null"); + + short flags = 0; + if (setRead) { + flags |= PARCEventType_Read; + } + + if (setWrite) { + flags |= PARCEventType_Write; + } + + parcEventQueue_SetWatermark(buffer, flags, low, high); +} + +int streamBuffer_Flush(PARCEventQueue *buffer, bool flushRead, + bool flushWrite) { + parcAssertNotNull(buffer, "Parameter buffer must be non-null"); + + short flags = 0; + if (flushRead) { + flags |= PARCEventType_Read; + } + + if (flushWrite) { + flags |= PARCEventType_Write; + } + + return parcEventQueue_Flush(buffer, flags); +} + +int streamBuffer_FlushCheckpoint(PARCEventQueue *buffer, bool flushRead, + bool flushWrite) { + parcAssertNotNull(buffer, "Parameter buffer must be non-null"); + + short flags = 0; + if (flushRead) { + flags |= PARCEventType_Read; + } + + if (flushWrite) { + flags |= PARCEventType_Write; + } + + return parcEventQueue_Flush(buffer, flags); +} + +int streamBuffer_FlushFinished(PARCEventQueue *buffer, bool flushRead, + bool flushWrite) { + parcAssertNotNull(buffer, "Parameter buffer must be non-null"); + + short flags = 0; + if (flushRead) { + flags |= PARCEventType_Read; + } + + if (flushWrite) { + flags |= PARCEventType_Write; + } + + return parcEventQueue_Flush(buffer, flags); +} + +void streamBuffer_SetCallbacks(PARCEventQueue *buffer, + PARCEventQueue_Callback *readCallback, + PARCEventQueue_Callback *writeCallback, + PARCEventQueue_EventCallback *eventCallback, + void *user_data) { + parcAssertNotNull(buffer, "Parameter buffer must be non-null"); + + parcEventQueue_SetCallbacks(buffer, readCallback, writeCallback, + eventCallback, user_data); +} + +void streamBuffer_EnableCallbacks(PARCEventQueue *buffer, bool enableRead, + bool enableWrite) { + parcAssertNotNull(buffer, "Parameter buffer must be non-null"); + short flags = 0; + if (enableRead) { + flags |= PARCEventType_Read; + } + if (enableWrite) { + flags |= PARCEventType_Write; + } + + parcEventQueue_Enable(buffer, flags); +} + +/** + * @function StreamBuffer_DisableCallbacks + * @abstract Disables specified callbacks. Does not affect others. + * @discussion + * Disables enabled callbacks. If a callback is already disabled, has no + * effect. A "false" value does not enable it. + * + * @param <#param1#> + * @return <#return#> + */ +void streamBuffer_DisableCallbacks(PARCEventQueue *buffer, bool disableRead, + bool disableWrite) { + parcAssertNotNull(buffer, "Parameter buffer must be non-null"); + short flags = 0; + if (disableRead) { + flags |= PARCEventType_Read; + } + if (disableWrite) { + flags |= PARCEventType_Write; + } + + parcEventQueue_Disable(buffer, flags); +} diff --git a/hicn-light/src/core/streamBuffer.h b/hicn-light/src/core/streamBuffer.h new file mode 100755 index 000000000..27e793176 --- /dev/null +++ b/hicn-light/src/core/streamBuffer.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Wrapper around event scheduler + */ + +#ifndef streamBuffer_h +#define streamBuffer_h + +#include +#include + +void streamBuffer_Destroy(PARCEventQueue **bufferPtr); + +/** + * @function streamBuffer_SetWatermark + * @abstract Sets the read and/or write watermarks + * @discussion + * For a read watermark, when there is at least low bytes + * available to read, the read callback will be fired. If the bytes in the + * buffer exceed high, the stream buffer will stop reading from the + * network. + * + * For a write watermark, when the bytes in the buffer fall below + * low, the write callback is fired. The high + * watermark limits stream filters and shapers from exceeding that threashold on + * what they write to the buffer. + * + */ +void streamBuffer_SetWatermark(PARCEventQueue *buffer, bool setRead, + bool setWrite, size_t low, size_t high); + +/** + * @function streamBuffer_Flush + * @abstract The buffer will read/write more data if available + * + * @return -1 error, 0 no more data, 1 more data + */ +int streamBuffer_Flush(PARCEventQueue *buffer, bool flushRead, bool flushWrite); + +/** + * @function streamBuffer_FlushCheckpoint + * @abstract Flushes the stream, checkpointing all data in the buffer + */ +int streamBuffer_FlushCheckpoint(PARCEventQueue *buffer, bool flushRead, + bool flushWrite); + +/** + * @function streamBuffer_FlushFinished + * @abstract Flush the stream and indicate the end of new data + */ +int streamBuffer_FlushFinished(PARCEventQueue *buffer, bool flushRead, + bool flushWrite); + +/** + * @typedef StreamBufferReadWriteCallback + * @abstract Callback when data is available or write space available + * @constant user_data opaque data passed to + * StreamBuffer_SetCallbacks() + */ +typedef void(StreamBufferReadWriteCallback)(PARCEventQueue *buffer, + void *user_data); + +/** + * @typedef StreamBufferEventCallback + * @abstract Callback on error or other event on the stream buffer + * @constant what logical or of STREAM events. STREAM_READING and + * STREAM_WRITING indicate if the error was on the read or write direction. The + * conditions may be STREAM_ERROR, STREAM_EOF, STREAM_TIMEOUT, or + * STREAM_CONNECTED. + * @constant user_data opaque data passed to + * StreamBuffer_SetCallbacks() + */ +typedef void(StreamBufferEventCallback)(PARCEventQueue *buffer, short what, + void *user_data); + +/** + * Changes the callbacks for a buffer event. + * + * @param bufev the buffer event object for which to change callbacks + * @param readcb callback to invoke when there is data to be read, or NULL if + * no callback is desired + * @param writecb callback to invoke when the file descriptor is ready for + * writing, or NULL if no callback is desired + * @param eventcb callback to invoke when there is an event on the file + * descriptor + * @param cbarg an argument that will be supplied to each of the callbacks + * (readcb, writecb, and errorcb) + * @see parcEventQueue_Create() + */ +void streamBuffer_SetCallbacks(PARCEventQueue *buffer, + PARCEventQueue_Callback *readCallback, + PARCEventQueue_Callback *writeCallback, + PARCEventQueue_EventCallback *eventCallback, + void *user_data); + +/** + * @function StreamBuffer_EnableCallbacks + * @abstract Enables specified callbacks. Does not affect others. + * @discussion + * Enables disabled callbacks. If a callback is already enabled, has no + * effect. A "false" value does not disable it. + */ +void streamBuffer_EnableCallbacks(PARCEventQueue *buffer, bool enableRead, + bool enableWrite); + +/** + * @function StreamBuffer_DisableCallbacks + * @abstract Disables specified callbacks. Does not affect others. + * @discussion + * Disables enabled callbacks. If a callback is already disabled, has no + * effect. A "false" value does not enable it. + */ +void streamBuffer_DisableCallbacks(PARCEventQueue *buffer, bool disableRead, + bool disableWrite); +#endif // streamBuffer_h diff --git a/hicn-light/src/core/system.h b/hicn-light/src/core/system.h new file mode 100755 index 000000000..3c5c8cba2 --- /dev/null +++ b/hicn-light/src/core/system.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @header system.h + * @abstract System-level properties + * @discussion + * <#Discussion#> + * + */ + +#ifndef system_h +#define system_h + +#include +#include + +/** + * @function system_Interfaces + * @abstract The system network interfaces + */ +InterfaceSet *system_Interfaces(Forwarder *forwarder); + +/** + * Returns the MTU of the named interface + * + * @param [in] an allocated hicn-light forwarder + * @param [in] interfaceName The system interface name, e.g. "eth0" + * + * @return 0 Interface does not exist + * @return positive the MTU the kernel reports + * + */ +unsigned system_InterfaceMtu(Forwarder *forwarder, const char *interfaceName); + +/** + * Returns the LINK address of the specified interface + * + * @param [in] an allocated hicn-light forwarder + * @param [in] interfaceName The system interface name, e.g. "eth0" + * + * @retval non-null The MAC address of the interface + * @retval null The interface does not exist + * + */ +Address *system_GetMacAddressByName(Forwarder *forwarder, + const char *interfaceName); +#endif diff --git a/hicn-light/src/core/ticks.h b/hicn-light/src/core/ticks.h new file mode 100755 index 000000000..8750abde5 --- /dev/null +++ b/hicn-light/src/core/ticks.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief The router periodically measures time in units of Ticks + * + * See forwarder.c HZ which specifies the tick rate. forwarder.h has functions + * to convert between ticks and milliseconds. + * + */ +#ifndef ticks_h +#define ticks_h + +#define __STDC_FORMAT_MACROS +#include + +typedef uint64_t Ticks; + +#endif // ticks_h diff --git a/hicn-light/src/core/wldr.c b/hicn-light/src/core/wldr.c new file mode 100755 index 000000000..b94ae76e5 --- /dev/null +++ b/hicn-light/src/core/wldr.c @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct wldr_buffer { + Message *message; + uint8_t rtx_counter; +}; + +typedef struct wldr_buffer WldrBuffer; + +struct wldr_state { + uint16_t expected_label; + uint16_t next_label; + WldrBuffer *buffer[BUFFER_SIZE]; +}; + +Wldr *wldr_Init() { + Wldr *wldr = parcMemory_AllocateAndClear(sizeof(Wldr)); + parcAssertNotNull(wldr, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Wldr)); + wldr->expected_label = 1; + wldr->next_label = 1; + for (int i = 0; i < BUFFER_SIZE; i++) { + WldrBuffer *entry = parcMemory_AllocateAndClear(sizeof(WldrBuffer)); + parcAssertNotNull( + entry, + "WldrBuffer init: parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(WldrBuffer)); + entry->message = NULL; + entry->rtx_counter = 0; + wldr->buffer[i] = entry; + } + return wldr; +} + +void wldr_ResetState(Wldr *wldr) { + wldr->expected_label = 1; + wldr->next_label = 1; + for (int i = 0; i < BUFFER_SIZE; i++) { + wldr->buffer[i]->message = NULL; + wldr->buffer[i]->rtx_counter = 0; + } +} + +void wldr_Destroy(Wldr **wldrPtr) { + Wldr *wldr = *wldrPtr; + for (unsigned i = 0; i < BUFFER_SIZE; i++) { + if (wldr->buffer[i]->message != NULL) { + message_Release(&(wldr->buffer[i]->message)); + parcMemory_Deallocate((void **)&(wldr->buffer[i])); + } + } + parcMemory_Deallocate((void **)&wldr); + *wldrPtr = NULL; +} + +static void _wldr_RetransmitPacket(Wldr *wldr, const Connection *conn, + uint16_t label) { + if (wldr->buffer[label % BUFFER_SIZE]->message == NULL) { + // the required message for retransmission is not in the buffer + return; + } + + if (wldr->buffer[label % BUFFER_SIZE]->rtx_counter < MAX_RTX) { + Message *msg = wldr->buffer[label % BUFFER_SIZE]->message; + message_SetWldrLabel(msg, wldr->next_label); + + if (wldr->buffer[wldr->next_label % BUFFER_SIZE]->message != NULL) { + message_Release(&(wldr->buffer[wldr->next_label % BUFFER_SIZE]->message)); + } + + wldr->buffer[wldr->next_label % BUFFER_SIZE]->message = msg; + wldr->buffer[wldr->next_label % BUFFER_SIZE]->rtx_counter = + wldr->buffer[label % BUFFER_SIZE]->rtx_counter + 1; + message_Acquire(wldr->buffer[wldr->next_label % BUFFER_SIZE]->message); + wldr->next_label++; + connection_ReSend(conn, msg, false); + } +} + +static void _wldr_SendWldrNotificaiton(Wldr *wldr, const Connection *conn, + Message *message, uint16_t expected_lbl, + uint16_t received_lbl) { + // here we need to create a new packet that is used to send the wldr + // notification to the prevoius hop. the destionation address of the + // notification is the source address of the message for which we want to + // create a notification. in fact, if message is an interest the prevoius hop + // is identified by the src. if message is a data, we need to send the + // notification message with the content name has a source address in this way + // the message will be trapped by the pounting rules in the next hop We define + // the notification as an interest message so that the NAT in the send function + // will set the src address of the local connection. Notice that in this way + // the notification packet will be dispaced to the right connection at the next + // hop. + + Message *notification = + message_CreateWldrNotification(message, expected_lbl, received_lbl); + parcAssertNotNull(notification, "Got null from CreateWldrNotification"); + connection_ReSend(conn, notification, true); +} + +void wldr_SetLabel(Wldr *wldr, Message *message) { + // in this function we send the packet for the first time + // 1) we set the wldr label + message_SetWldrLabel(message, wldr->next_label); + + // 2) we store the pointer to packet in the buffer + if (wldr->buffer[wldr->next_label % BUFFER_SIZE]->message != NULL) { + // release an old message if necessary + message_Release(&(wldr->buffer[wldr->next_label % BUFFER_SIZE]->message)); + } + + // we need to acquire the message to avoid that it gets destroyed + message_Acquire(message); + + wldr->buffer[wldr->next_label % BUFFER_SIZE]->message = message; + wldr->buffer[wldr->next_label % BUFFER_SIZE]->rtx_counter = 0; + wldr->next_label++; + if (wldr->next_label == + 0) // we alwasy skip label 0 beacause it means that wldr is not active + wldr->next_label++; +} + +void wldr_DetectLosses(Wldr *wldr, const Connection *conn, Message *message) { + if (message_HasWldr(message)) { + // this is a normal wldr packet + uint16_t pkt_lbl = (uint16_t)message_GetWldrLabel(message); + if (pkt_lbl != wldr->expected_label) { + // if the received packet label is 1 and the expected packet label > + // pkt_lbl usually we are in the case where a remote note disconnected for + // a while and reconnected on this same connection, so the two nodes are + // out of synch for this reason we do not send any notification, we just + // synch the labels + + if ((pkt_lbl != 1) || (wldr->expected_label < pkt_lbl)) { + _wldr_SendWldrNotificaiton(wldr, conn, message, wldr->expected_label, + pkt_lbl); + } + + // here we always synch + wldr->expected_label = (uint16_t)(pkt_lbl + 1); + } else { + wldr->expected_label++; + if (wldr->expected_label == 0) + wldr->expected_label++; // for the next_label we want to skip 0 + } + } +} + +void wldr_HandleWldrNotification(Wldr *wldr, const Connection *conn, + Message *message) { + uint16_t expected_lbl = (uint16_t)message_GetWldrExpectedLabel(message); + uint16_t received_lbl = (uint16_t)message_GetWldrLastReceived(message); + if ((wldr->next_label - expected_lbl) > BUFFER_SIZE) { + // the packets are not in the buffer anymore + return; + } + while (expected_lbl < received_lbl) { + _wldr_RetransmitPacket(wldr, conn, expected_lbl); + expected_lbl++; + } +} diff --git a/hicn-light/src/core/wldr.h b/hicn-light/src/core/wldr.h new file mode 100755 index 000000000..1666b4d3f --- /dev/null +++ b/hicn-light/src/core/wldr.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef wldr_h +#define wldr_h + +#include +#include +#include + +#define BUFFER_SIZE 8192 +#define MAX_RTX 3 +#define WLDR_LBL 13 +#define WLDR_NOTIFICATION 14 +#define WLDR_UNKNOWN 15 + +// NORMAL PACKET or RETRASMISSION +// WLDR_LBL: label = window size in the TCP header +// NOTIFICATION +// WLDR_NOTIFICATION: expected_label = window size in the TCP header, +// last_received_label = urgent pointer in the TCP header +// ATTENTION!!! in order to detect a notificaiton the +// source and destination ports must be set to 0 + +struct wldr_state; +typedef struct wldr_state Wldr; + +Wldr *wldr_Init(); + +void wldr_Destroy(Wldr **wldrPtr); + +void wldr_ResetState(Wldr *wldr); + +void wldr_SetLabel(Wldr *wldr, Message *message); + +void wldr_DetectLosses(Wldr *wldr, const Connection *conn, Message *message); + +void wldr_HandleWldrNotification(Wldr *wldr, const Connection *conn, + Message *message); +#endif // wldr_h diff --git a/hicn-light/src/io/CMakeLists.txt b/hicn-light/src/io/CMakeLists.txt new file mode 100755 index 000000000..f65f0b580 --- /dev/null +++ b/hicn-light/src/io/CMakeLists.txt @@ -0,0 +1,53 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/addressPair.h + ${CMAKE_CURRENT_SOURCE_DIR}/ioOperations.h + ${CMAKE_CURRENT_SOURCE_DIR}/listener.h + ${CMAKE_CURRENT_SOURCE_DIR}/listenerSet.h + ${CMAKE_CURRENT_SOURCE_DIR}/tcpListener.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicnListener.h + ${CMAKE_CURRENT_SOURCE_DIR}/udpTunnel.h + ${CMAKE_CURRENT_SOURCE_DIR}/tcpTunnel.h + ${CMAKE_CURRENT_SOURCE_DIR}/udpConnection.h + ${CMAKE_CURRENT_SOURCE_DIR}/udpListener.h + ${CMAKE_CURRENT_SOURCE_DIR}/streamConnection.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicnTunnel.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicnConnection.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/addressPair.c + ${CMAKE_CURRENT_SOURCE_DIR}/ioOperations.c + ${CMAKE_CURRENT_SOURCE_DIR}/listenerSet.c + ${CMAKE_CURRENT_SOURCE_DIR}/streamConnection.c + ${CMAKE_CURRENT_SOURCE_DIR}/tcpListener.c + ${CMAKE_CURRENT_SOURCE_DIR}/tcpTunnel.c + ${CMAKE_CURRENT_SOURCE_DIR}/udpConnection.c + ${CMAKE_CURRENT_SOURCE_DIR}/udpListener.c + ${CMAKE_CURRENT_SOURCE_DIR}/udpTunnel.c +) + +if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + list(APPEND SOURCE_FILES + io/hicnTunnel.c + io/hicnConnection.c + io/hicnListener.c + ) +endif() + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/hicn-light/src/io/addressPair.c b/hicn-light/src/io/addressPair.c new file mode 100755 index 000000000..5d2017a3d --- /dev/null +++ b/hicn-light/src/io/addressPair.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include +#include +#include + +struct address_pair { + Address *local; + Address *remote; +}; + +static void _addressPair_Destroy(AddressPair **addressPairPtr) { + AddressPair *pair = *addressPairPtr; + + addressDestroy(&pair->local); + addressDestroy(&pair->remote); +} + +parcObject_ExtendPARCObject(AddressPair, _addressPair_Destroy, NULL, + addressPair_ToString, addressPair_Equals, NULL, + addressPair_HashCode, NULL); + +parcObject_ImplementAcquire(addressPair, AddressPair); + +parcObject_ImplementRelease(addressPair, AddressPair); + +AddressPair *addressPair_Create(const Address *local, const Address *remote) { + parcAssertNotNull(local, "Parameter local must be non-null"); + parcAssertNotNull(remote, "Parameter remote must be non-null"); + + AddressPair *pair = parcObject_CreateInstance(AddressPair); + parcAssertNotNull(pair, "Got null from parcObject_Create()"); + + pair->local = addressCopy(local); + pair->remote = addressCopy(remote); + + return pair; +} + +bool addressPair_Equals(const AddressPair *a, const AddressPair *b) { + if (a == b) { + return true; + } + if (a == NULL || b == NULL) { + return false; + } + + if (addressEquals(a->local, b->local)) { + if (addressEquals(a->remote, b->remote)) { + return true; + } + } + + return false; +} + +bool addressPair_EqualsAddresses(const AddressPair *a, const Address *local, + const Address *remote) { + if (a == NULL || local == NULL || remote == NULL) { + return false; + } + + if (addressEquals(a->local, local)) { + if (addressEquals(a->remote, remote)) { + return true; + } + } + + return false; +} + +char *addressPair_ToString(const AddressPair *pair) { + parcAssertNotNull(pair, "Parameter pair must be non-null"); + + char *local = addressToString(pair->local); + char *remote = addressToString(pair->remote); + + char *output; + int failure = asprintf(&output, "{ .local=%s, .remote=%s }", local, remote); + parcAssertTrue(failure > -1, "Error on asprintf"); + + parcMemory_Deallocate((void **)&local); + parcMemory_Deallocate((void **)&remote); + + return output; +} + +const Address *addressPair_GetLocal(const AddressPair *pair) { + parcAssertNotNull(pair, "Parameter pair must be non-null"); + return pair->local; +} + +const Address *addressPair_GetRemote(const AddressPair *pair) { + parcAssertNotNull(pair, "Parameter pair must be non-null"); + return pair->remote; +} + +/** + * @function addressPair_HashCode + * @abstract Hash useful for tables. Consistent with Equals. + * @discussion + * Returns a non-cryptographic hash that is consistent with equals. That is, + * if a == b, then hash(a) == hash(b). + * + */ +PARCHashCode addressPair_HashCode(const AddressPair *pair) { + PARCHashCode hashpair[2]; + hashpair[0] = addressHashCode(pair->local); + hashpair[1] = addressHashCode(pair->remote); + return parcHashCode_Hash((const uint8_t *)hashpair, sizeof(hashpair)); +} diff --git a/hicn-light/src/io/addressPair.h b/hicn-light/src/io/addressPair.h new file mode 100755 index 000000000..5152267b6 --- /dev/null +++ b/hicn-light/src/io/addressPair.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Used to identify a connection between a specific local address and + * a specific remote address. + */ + +#ifndef address_Pair_h +#define address_Pair_h + +#include + +struct address_pair; +typedef struct address_pair AddressPair; + +/** + * @function addressPair_Create + * @abstract Creates and address pair. There is no restriction on the address + * types. + * @discussion + * Creates an ordered pair of addresses, where the first is considered the + * "local" address and the second is the "remote" address. Those designations + * are purely a convention used to name them, and does not imply any specifici + * types of operations. + * + * The two addresses may be of any address types (e.g. IPv4, IPv6, Local, + * Ethernet). However, some functions that use an AddressPair may require that + * the local and remote addresses be the same type. + * + */ +AddressPair *addressPair_Create(const Address *local, const Address *remote); + +/** + * Returns a reference counted copy of the address pair + * + * Increments the reference count and returns the same address pair + * + * @param [in] addressPair An allocated address pair + * + * @retval non-null A reference counted copy + * @retval null An error + */ +AddressPair *addressPair_Acquire(const AddressPair *addressPair); + +/** + * Releases a reference count to the object + * + * Decrements the reference count and destroys the object when it reaches 0. + */ +void addressPair_Release(AddressPair **pairPtr); + +/** + * Determine if two AddressPair instances are equal. + * + * Two AddressPair instances are equal if, and only if, the local and remote + * addresses are identical. Equality is determined by addressEquals(a->local, + * b->local) and Adress_Equals(a->remote, b->remote). + * + * The following equivalence relations on non-null `AddressPair` instances are + * maintained: + * + * * It is reflexive: for any non-null reference value x, + * `AddressPair_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `addressPair_Equals(x, y)` must return true if and only if + * `addressPair_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `addressPair_Equals(x, y)` returns true and + * `addressPair_Equals(y, z)` returns true, + * then `addressPair_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `addressPair_Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `addressPair_Equals(x, NULL)` must + * return false. + * + * @param a A pointer to a `AddressPair` instance. + * @param b A pointer to a `AddressPair` instance. + * @return true if the two `AddressPair` instances are equal. + */ +bool addressPair_Equals(const AddressPair *a, const AddressPair *b); + +/** + * @function addressPair_EqualsAddresses + * @abstract As AddressEquals, but "b" is broken out + * @discussion + * Equality is determined by addressEquals(a->local, local) and + * Adress_Equals(a->remote, remote). + */ +bool addressPair_EqualsAddresses(const AddressPair *a, const Address *local, + const Address *remote); + +const Address *addressPair_GetLocal(const AddressPair *pair); + +const Address *addressPair_GetRemote(const AddressPair *pair); + +/** + * @function addressPair_HashCode + * @abstract Hash useful for tables. Consistent with Equals. + * @discussion + * Returns a non-cryptographic hash that is consistent with equals. That is, + * if a == b, then hash(a) == hash(b). + */ +PARCHashCode addressPair_HashCode(const AddressPair *pair); + +/** + * @function addressPair_ToString + * @abstract Human readable string representation. Caller must use free(3). + */ +char *addressPair_ToString(const AddressPair *pair); +#endif // address_Pair_h diff --git a/hicn-light/src/io/hicnConnection.c b/hicn-light/src/io/hicnConnection.c new file mode 100755 index 000000000..85cf50921 --- /dev/null +++ b/hicn-light/src/io/hicnConnection.c @@ -0,0 +1,517 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Embodies the reader/writer for a HIcn connection + * + * NB The Send() function may overflow the output buffer + * + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +typedef struct hicn_state { + Forwarder *forwarder; + Logger *logger; + + // the hicn listener socket we receive packets on + int hicnListenerSocket; + + AddressPair *addressPair; + + // We need to access this all the time, so grab it out + // of the addressPair; + struct sockaddr *peerAddress; + socklen_t peerAddressLength; + + struct sockaddr *localAddress; + socklen_t localAddressLength; + + // this address contains one of the content names reachable + // throught the connection peer. We need this address beacuse it is + // the only way we have to conntact the next hop, since the main tun + // does not have address. Notice that a connection that sends probes + // is a connection that sends interest. In a "data" connection this + // value will remain NULL. We refresh the content address every time + // we send a probe, in this way we don't need to waste to much time in + // copy the address, but we can also react to the routing changes + struct sockaddr *probeDestAddress; + socklen_t probeDestAddressLength; + bool refreshProbeDestAddress; + + bool isLocal; + bool isUp; + unsigned id; + + unsigned delay; +} _HicnState; + +// Prototypes +static bool _send(IoOperations *ops, const Address *nexthop, Message *message); +static const Address *_getRemoteAddress(const IoOperations *ops); +static const AddressPair *_getAddressPair(const IoOperations *ops); +static unsigned _getConnectionId(const IoOperations *ops); +static bool _isUp(const IoOperations *ops); +static bool _isLocal(const IoOperations *ops); +static void _destroy(IoOperations **opsPtr); +static list_connections_type _getConnectionType(const IoOperations *ops); +static Ticks _sendProbe(IoOperations *ops, unsigned probeType, + uint8_t *message); + +/* + * This assigns a unique pointer to the void * which we use + * as a GUID for this class. + */ +static const void *_ioOperationsGuid = __FILE__; + +/* + * Return our GUID + */ +static const void *_streamConnection_Class(const IoOperations *ops) { + return _ioOperationsGuid; +} + +static IoOperations _template = {.closure = NULL, + .send = &_send, + .getRemoteAddress = &_getRemoteAddress, + .getAddressPair = &_getAddressPair, + .getConnectionId = &_getConnectionId, + .isUp = &_isUp, + .isLocal = &_isLocal, + .destroy = &_destroy, + .class = &_streamConnection_Class, + .getConnectionType = &_getConnectionType, + .sendProbe = &_sendProbe}; + +// ================================================================= + +static void _setConnectionState(_HicnState *HIcn, bool isUp); +static bool _saveSockaddr(_HicnState *hicnConnState, const AddressPair *pair); +static void _refreshProbeDestAddress(_HicnState *hicnConnState, + const uint8_t *message); + +IoOperations *hicnConnection_Create(Forwarder *forwarder, int fd, + const AddressPair *pair, bool isLocal) { + IoOperations *io_ops = NULL; + + _HicnState *hicnConnState = parcMemory_AllocateAndClear(sizeof(_HicnState)); + parcAssertNotNull(hicnConnState, + "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(_HicnState)); + + hicnConnState->forwarder = forwarder; + hicnConnState->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + + bool saved = _saveSockaddr(hicnConnState, pair); + if (saved) { + hicnConnState->hicnListenerSocket = fd; + hicnConnState->id = forwarder_GetNextConnectionId(forwarder); + hicnConnState->addressPair = addressPair_Acquire(pair); + hicnConnState->isLocal = isLocal; + + // allocate a connection + io_ops = parcMemory_AllocateAndClear(sizeof(IoOperations)); + parcAssertNotNull(io_ops, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(IoOperations)); + memcpy(io_ops, &_template, sizeof(IoOperations)); + io_ops->closure = hicnConnState; + + _setConnectionState(hicnConnState, true); + + if (logger_IsLoggable(hicnConnState->logger, LoggerFacility_IO, + PARCLogLevel_Info)) { + char *str = addressPair_ToString(hicnConnState->addressPair); + logger_Log(hicnConnState->logger, LoggerFacility_IO, PARCLogLevel_Info, + __func__, + "HIcnConnection %p created for address %s (isLocal %d)", + (void *)hicnConnState, str, hicnConnState->isLocal); + free(str); + } + + messenger_Send( + forwarder_GetMessenger(forwarder), + missive_Create(MissiveType_ConnectionCreate, hicnConnState->id)); + messenger_Send(forwarder_GetMessenger(forwarder), + missive_Create(MissiveType_ConnectionUp, hicnConnState->id)); + } else { + // _saveSockaddr will already log an error, no need for extra log message + // here + logger_Release(&hicnConnState->logger); + parcMemory_Deallocate((void **)&hicnConnState); + } + + return io_ops; +} + +// ================================================================= +// I/O Operations implementation + +static void _destroy(IoOperations **opsPtr) { + parcAssertNotNull(opsPtr, "Parameter opsPtr must be non-null double pointer"); + parcAssertNotNull(*opsPtr, + "Parameter opsPtr must dereference to non-null pointer"); + + IoOperations *ops = *opsPtr; + parcAssertNotNull(ioOperations_GetClosure(ops), + "ops->context must not be null"); + + _HicnState *hicnConnState = (_HicnState *)ioOperations_GetClosure(ops); + addressPair_Release(&hicnConnState->addressPair); + parcMemory_Deallocate((void **)&(hicnConnState->peerAddress)); + parcMemory_Deallocate((void **)&(hicnConnState->localAddress)); + if (hicnConnState->probeDestAddress != NULL) + parcMemory_Deallocate((void **)&(hicnConnState->probeDestAddress)); + + messenger_Send( + forwarder_GetMessenger(hicnConnState->forwarder), + missive_Create(MissiveType_ConnectionDestroyed, hicnConnState->id)); + + if (logger_IsLoggable(hicnConnState->logger, LoggerFacility_IO, + PARCLogLevel_Info)) { + logger_Log(hicnConnState->logger, LoggerFacility_IO, PARCLogLevel_Info, + __func__, "HIcnConnection %p destroyed", (void *)hicnConnState); + } + + // XXX + // do not close hicListenerSocket, the listener will close + // that when its done + // should I say something to libhicn? + + logger_Release(&hicnConnState->logger); + parcMemory_Deallocate((void **)&hicnConnState); + parcMemory_Deallocate((void **)&ops); + + *opsPtr = NULL; +} + +static bool _isUp(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _HicnState *hicnConnState = + (const _HicnState *)ioOperations_GetClosure(ops); + return hicnConnState->isUp; +} + +static bool _isLocal(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _HicnState *hicnConnState = + (const _HicnState *)ioOperations_GetClosure(ops); + return hicnConnState->isLocal; +} + +static const Address *_getRemoteAddress(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _HicnState *hicnConnState = + (const _HicnState *)ioOperations_GetClosure(ops); + return addressPair_GetRemote(hicnConnState->addressPair); +} + +static const AddressPair *_getAddressPair(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _HicnState *hicnConnState = + (const _HicnState *)ioOperations_GetClosure(ops); + return hicnConnState->addressPair; +} + +static unsigned _getConnectionId(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _HicnState *hicnConnState = + (const _HicnState *)ioOperations_GetClosure(ops); + return hicnConnState->id; +} + +/** + * @function hicnConnection_Send + * @abstract Non-destructive send of the message. + * @discussion + * sends a message to the peer. + * + * @param dummy is ignored. . + * @return <#return#> + */ +static bool _send(IoOperations *ops, const Address *dummy, Message *message) { + parcAssertNotNull(ops, "Parameter ops must be non-null"); + parcAssertNotNull(message, "Parameter message must be non-null"); + _HicnState *hicnConnState = (_HicnState *)ioOperations_GetClosure(ops); + + // NAT for HICN + // XXX + if (message_GetType(message) == MessagePacketType_ContentObject) { + // this is a data packet. We need to put the remote address in the + // destination field + + if (messageHandler_GetIPPacketType(message_FixedHeader(message)) == + IPv6_TYPE) { + messageHandler_SetDestination_IPv6( + (uint8_t *)message_FixedHeader(message), + &((struct sockaddr_in6 *)hicnConnState->peerAddress)->sin6_addr); + } else { + messageHandler_SetDestination_IPv4( + (uint8_t *)message_FixedHeader(message), + &(((struct sockaddr_in *)hicnConnState->peerAddress) + ->sin_addr.s_addr)); + } + } else if (message_GetType(message) == MessagePacketType_Interest) { + // this si an interest packet. We need to put the local address in the + // source field + if (messageHandler_GetIPPacketType(message_FixedHeader(message)) == + IPv6_TYPE) { + messageHandler_SetSource_IPv6( + (uint8_t *)message_FixedHeader(message), + &((struct sockaddr_in6 *)hicnConnState->localAddress)->sin6_addr); + } else { + messageHandler_SetSource_IPv4( + (uint8_t *)message_FixedHeader(message), + &(((struct sockaddr_in *)hicnConnState->localAddress) + ->sin_addr.s_addr)); + } + + // only in this case we may need to set the probeDestAddress + if (hicnConnState->refreshProbeDestAddress) { + _refreshProbeDestAddress(hicnConnState, message_FixedHeader(message)); + } + + } else if (message_GetType(message) == MessagePacketType_WldrNotification) { + // here we don't need to do anything for now + } else { + // unkown packet + if (logger_IsLoggable(hicnConnState->logger, LoggerFacility_IO, + PARCLogLevel_Debug)) { + logger_Log(hicnConnState->logger, LoggerFacility_IO, PARCLogLevel_Debug, + __func__, "connid %u can't parse the message", + hicnConnState->id); + } + return false; + } + + ssize_t writeLength = + write(hicnConnState->hicnListenerSocket, message_FixedHeader(message), + message_Length(message)); + + if (writeLength < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return false; + } else { + // this print is for debugging + printf("Incorrect write length %zd, expected %zd: (%d) %s\n", writeLength, + message_Length(message), errno, strerror(errno)); + return false; + } + } + + return true; +} + +static list_connections_type _getConnectionType(const IoOperations *ops) { + return CONN_HICN; +} + +static Ticks _sendProbe(IoOperations *ops, unsigned probeType, + uint8_t *message) { + parcAssertNotNull(ops, "Parameter ops must be non-null"); + _HicnState *hicnConnState = (_HicnState *)ioOperations_GetClosure(ops); + + if ((hicnConnState->peerAddressLength == sizeof(struct sockaddr_in)) || + (hicnConnState->localAddressLength == sizeof(struct sockaddr_in))) + return false; + + if (hicnConnState->probeDestAddress == NULL && + probeType == PACKET_TYPE_PROBE_REPLY) { + uint8_t *pkt = parcMemory_AllocateAndClear( + messageHandler_GetICMPPacketSize(IPv6_TYPE)); + messageHandler_SetProbePacket( + pkt, probeType, + (struct in6_addr *)messageHandler_GetDestination(message), + (struct in6_addr *)messageHandler_GetSource(message)); + + ssize_t writeLength = write(hicnConnState->hicnListenerSocket, pkt, + messageHandler_GetICMPPacketSize(IPv6_TYPE)); + + parcMemory_Deallocate((void **)&pkt); + + if (writeLength < 0) { + return 0; + } + + } else if (hicnConnState->probeDestAddress != NULL && + probeType == PACKET_TYPE_PROBE_REQUEST) { + hicnConnState->refreshProbeDestAddress = true; + + uint8_t *pkt = parcMemory_AllocateAndClear( + messageHandler_GetICMPPacketSize(IPv6_TYPE)); + messageHandler_SetProbePacket( + pkt, probeType, + &((struct sockaddr_in6 *)hicnConnState->localAddress)->sin6_addr, + &((struct sockaddr_in6 *)hicnConnState->probeDestAddress)->sin6_addr); + + ssize_t writeLength = write(hicnConnState->hicnListenerSocket, pkt, + messageHandler_GetICMPPacketSize(IPv6_TYPE)); + + parcMemory_Deallocate((void **)&pkt); + + if (writeLength < 0) { + return 0; + } + + } else { + if (hicnConnState->probeDestAddress == NULL && + probeType == PACKET_TYPE_PROBE_REQUEST) { + // this happen for the first probe + hicnConnState->refreshProbeDestAddress = true; + } + // do nothing + return 0; + } + + return forwarder_GetTicks(hicnConnState->forwarder); +} + +// ================================================================= +// Internal API + +static bool _saveSockaddr(_HicnState *hicnConnState, const AddressPair *pair) { + bool success = false; + const Address *remoteAddress = addressPair_GetRemote(pair); + const Address *localAddress = addressPair_GetLocal(pair); + switch (addressGetType(remoteAddress)) { // local must be of the same type + + case ADDR_INET: { + size_t bytes = sizeof(struct sockaddr_in); + hicnConnState->peerAddress = parcMemory_Allocate(bytes); + parcAssertNotNull(hicnConnState->peerAddress, + "parcMemory_Allocate(%zu) returned NULL", bytes); + + addressGetInet(remoteAddress, + (struct sockaddr_in *)hicnConnState->peerAddress); + hicnConnState->peerAddressLength = (socklen_t)bytes; + + hicnConnState->localAddress = parcMemory_Allocate(bytes); + parcAssertNotNull(hicnConnState->localAddress, + "parcMemory_Allocate(%zu) returned NULL", bytes); + + addressGetInet(localAddress, + (struct sockaddr_in *)hicnConnState->localAddress); + hicnConnState->localAddressLength = (socklen_t)bytes; + + hicnConnState->probeDestAddress = NULL; + hicnConnState->probeDestAddressLength = (socklen_t)bytes; + hicnConnState->refreshProbeDestAddress = false; + + success = true; + break; + } + + case ADDR_INET6: { + size_t bytes = sizeof(struct sockaddr_in6); + hicnConnState->peerAddress = parcMemory_Allocate(bytes); + parcAssertNotNull(hicnConnState->peerAddress, + "parcMemory_Allocate(%zu) returned NULL", bytes); + + addressGetInet6(remoteAddress, + (struct sockaddr_in6 *)hicnConnState->peerAddress); + hicnConnState->peerAddressLength = (socklen_t)bytes; + + hicnConnState->localAddress = parcMemory_Allocate(bytes); + parcAssertNotNull(hicnConnState->localAddress, + "parcMemory_Allocate(%zu) returned NULL", bytes); + + addressGetInet6(localAddress, + (struct sockaddr_in6 *)hicnConnState->localAddress); + hicnConnState->localAddressLength = (socklen_t)bytes; + + hicnConnState->probeDestAddress = NULL; + hicnConnState->probeDestAddressLength = (socklen_t)bytes; + hicnConnState->refreshProbeDestAddress = false; + + success = true; + break; + } + + default: + if (logger_IsLoggable(hicnConnState->logger, LoggerFacility_IO, + PARCLogLevel_Error)) { + char *str = addressToString(remoteAddress); + logger_Log(hicnConnState->logger, LoggerFacility_IO, PARCLogLevel_Error, + __func__, "Remote address is not INET or INET6: %s", str); + parcMemory_Deallocate((void **)&str); + } + break; + } + return success; +} + +static void _refreshProbeDestAddress(_HicnState *hicnConnState, + const uint8_t *message) { + if ((hicnConnState->peerAddressLength == sizeof(struct sockaddr_in)) || + (hicnConnState->localAddressLength == sizeof(struct sockaddr_in))) + return; + + if (hicnConnState->probeDestAddress == NULL) { + hicnConnState->probeDestAddress = + parcMemory_AllocateAndClear(sizeof(struct sockaddr_in6)); + parcAssertNotNull(hicnConnState->probeDestAddress, + "parcMemory_Allocate(%zu) returned NULL", + sizeof(struct sockaddr_in6)); + } + + ((struct sockaddr_in6 *)hicnConnState->probeDestAddress)->sin6_family = + AF_INET6; + ((struct sockaddr_in6 *)hicnConnState->probeDestAddress)->sin6_port = + htons(1234); + ((struct sockaddr_in6 *)hicnConnState->probeDestAddress)->sin6_scope_id = 0; + ((struct sockaddr_in6 *)hicnConnState->probeDestAddress)->sin6_flowinfo = 0; + ((struct sockaddr_in6 *)hicnConnState->probeDestAddress)->sin6_addr = + *((struct in6_addr *)messageHandler_GetDestination(message)); + hicnConnState->refreshProbeDestAddress = false; +} + +static void _setConnectionState(_HicnState *hicnConnState, bool isUp) { + parcAssertNotNull(hicnConnState, "Parameter HICN must be non-null"); + + Messenger *messenger = forwarder_GetMessenger(hicnConnState->forwarder); + + bool oldStateIsUp = hicnConnState->isUp; + hicnConnState->isUp = isUp; + + if (oldStateIsUp && !isUp) { + // bring connection DOWN + Missive *missive = + missive_Create(MissiveType_ConnectionDown, hicnConnState->id); + messenger_Send(messenger, missive); + return; + } + + if (!oldStateIsUp && isUp) { + // bring connection UP + Missive *missive = + missive_Create(MissiveType_ConnectionUp, hicnConnState->id); + messenger_Send(messenger, missive); + return; + } +} diff --git a/hicn-light/src/io/hicnConnection.h b/hicn-light/src/io/hicnConnection.h new file mode 100755 index 000000000..757e534ba --- /dev/null +++ b/hicn-light/src/io/hicnConnection.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file hicnConnection.h + * @brief Represents a HIcn connection for the connection table + * + * <#Detailed Description#> + * + */ + +#ifndef hicnConnection_h +#define hicnConnection_h + +#include +#include +#include +#include + +/** + * Creates a HIcn connection that can send to the remote address + * + * The address pair must both be same type (i.e. INET or INET6). + * + * @param [in] an allocated hicn-light Forwarder (saves reference) + * @param [in] fd The socket to use + * @param [in] pair An allocated address pair for the connection (saves + * reference) + * @param [in] isLocal determines if the remote address is on the current system + * + * @retval non-null An allocated Io operations + * @retval null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +IoOperations *hicnConnection_Create(Forwarder *forwarder, int fd, + const AddressPair *pair, bool isLocal); +#endif // hicnConnection_h diff --git a/hicn-light/src/io/hicnListener.c b/hicn-light/src/io/hicnListener.c new file mode 100755 index 000000000..161f5b317 --- /dev/null +++ b/hicn-light/src/io/hicnListener.c @@ -0,0 +1,725 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#ifdef WITH_MAPME +#include +#include +#include +#include +#endif /* WITH_MAPME */ +#include +#include +#include +#include +#include +#include +#include + +#define IPv6 6 +#define IPv4 4 +#define MTU_SIZE 1500 // bytes +#define MAX_HICN_RETRY 5 + +struct hicn_listener { + Forwarder *forwarder; + Logger *logger; + + PARCEvent *hicn_event; + int hicn_fd; // this is the file descriptor got from hicn library + + Address *localAddress; // this is the local address or 0::0 in case of the + // main listener this is the address used inside + // forwarder to identify the listener. Notice that this + // address is the same as the fisical interfaces on + // which we create the TUN. it is NOT the TUN address + // which is given by libhicn after the bind operation + // However the user alway uses this address since is + // the only one available at configuration time + + unsigned inetFamily; + + int connection_id; // this is used only if the listener is used to receive + // data packets we assume that 1 connection is associated + // to one listener in this case so we set the connection_id + // we the connection is create. if this id is not set and a + // data packet is received, the packet is dropped + + unsigned conn_id; +}; + +static void _destroy(ListenerOps **listenerOpsPtr); +static unsigned _getInterfaceIndex(const ListenerOps *ops); +static const Address *_getListenAddress(const ListenerOps *ops); +static EncapType _getEncapType(const ListenerOps *ops); +static int _getSocket(const ListenerOps *ops); + +static ListenerOps _hicnTemplate = {.context = NULL, + .destroy = &_destroy, + .getInterfaceIndex = &_getInterfaceIndex, + .getListenAddress = &_getListenAddress, + .getEncapType = &_getEncapType, + .getSocket = &_getSocket}; + +static void _hicnListener_readcb(int fd, PARCEventType what, void *hicnVoid); + +static bool _isEmptyAddressIPv6(Address *address) { + struct sockaddr_in6 *addr6 = + parcMemory_AllocateAndClear(sizeof(struct sockaddr_in6)); + parcAssertNotNull(addr6, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(addr6)); + + addressGetInet6(address, addr6); + + bool res = true; + for (int i = 0; i < 16; ++i) { + if (addr6->sin6_addr.s6_addr[i] != 0) { + res = false; + } + } + + parcMemory_Deallocate((void **)&addr6); + + return res; +} + +static bool _isEmptyAddressIPv4(Address *address) { + bool res = false; + + if (strcmp("inet4://0.0.0.0:1234", addressToString(address)) == 0) res = true; + return res; +} + +ListenerOps *hicnListener_CreateInet(Forwarder *forwarder, char *symbolic, + Address *address) { + HIcnListener *hicn = parcMemory_AllocateAndClear(sizeof(HIcnListener)); + parcAssertNotNull(hicn, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(HIcnListener)); + + hicn->forwarder = forwarder; + hicn->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + + hicn->conn_id = forwarder_GetNextConnectionId(forwarder); + hicn->localAddress = addressCopy(address); + + hicn->inetFamily = IPv4; + + hicn->connection_id = -1; + + hicn_socket_helper_t *hicnSocketHelper = + forwarder_GetHIcnSocketHelper(forwarder); + + if (_isEmptyAddressIPv4(address)) { + hicn->hicn_fd = hicn_socket(hicnSocketHelper, symbolic, NULL); + } else { + struct sockaddr_in *tmpAddr = + parcMemory_AllocateAndClear(sizeof(struct sockaddr_in)); + parcAssertNotNull(tmpAddr, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(tmpAddr)); + addressGetInet(address, tmpAddr); + char *local_addr = parcMemory_AllocateAndClear(INET_ADDRSTRLEN); + inet_ntop(AF_INET, &(tmpAddr->sin_addr), local_addr, INET_ADDRSTRLEN); + parcMemory_Deallocate((void **)&tmpAddr); + + hicn->hicn_fd = hicn_socket(hicnSocketHelper, symbolic, local_addr); + + parcMemory_Deallocate((void **)&local_addr); + } + + if (hicn->hicn_fd < 0) { + if (logger_IsLoggable(hicn->logger, LoggerFacility_IO, + PARCLogLevel_Debug)) { + logger_Log( + hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "HIcnListener %s: error while creating an hicn listener in lib_hicn", + symbolic); + } + logger_Release(&hicn->logger); + addressDestroy(&hicn->localAddress); + parcMemory_Deallocate((void **)&hicn); + return NULL; + } + + // Set non-blocking flag + int flags = fcntl(hicn->hicn_fd, F_GETFL, NULL); + parcAssertTrue(flags != -1, + "fcntl failed to obtain file descriptor flags (%d)", errno); + int failure = fcntl(hicn->hicn_fd, F_SETFL, flags | O_NONBLOCK); + parcAssertFalse(failure, "fcntl failed to set file descriptor flags (%d)", + errno); + + hicn->hicn_event = dispatcher_CreateNetworkEvent( + forwarder_GetDispatcher(forwarder), true, _hicnListener_readcb, + (void *)hicn, hicn->hicn_fd); + dispatcher_StartNetworkEvent(forwarder_GetDispatcher(forwarder), + hicn->hicn_event); + + ListenerOps *ops = parcMemory_AllocateAndClear(sizeof(ListenerOps)); + parcAssertNotNull(ops, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ListenerOps)); + + memcpy(ops, &_hicnTemplate, sizeof(ListenerOps)); + ops->context = hicn; + + if (logger_IsLoggable(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + logger_Log(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "HIcnListener %s created", symbolic); + } + + return ops; + return NULL; +} + +ListenerOps *hicnListener_CreateInet6(Forwarder *forwarder, char *symbolic, + Address *address) { + HIcnListener *hicn = parcMemory_AllocateAndClear(sizeof(HIcnListener)); + parcAssertNotNull(hicn, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(HIcnListener)); + + hicn->forwarder = forwarder; + hicn->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + + hicn->conn_id = forwarder_GetNextConnectionId(forwarder); + hicn->localAddress = addressCopy(address); + + hicn->inetFamily = IPv6; + + hicn->connection_id = -1; + + // the call to libhicn is the same both for the main and the normal listeners + // in both cases we need to set only the identifier. In the case of normal + // listener (listener for data packet) we let the library select the right ip + //address we just need to set the right type of packet + + hicn_socket_helper_t *hicnSocketHelper = + forwarder_GetHIcnSocketHelper(forwarder); + + if (_isEmptyAddressIPv6(address)) { + // create main listener + hicn->hicn_fd = hicn_socket(hicnSocketHelper, symbolic, NULL); + } else { + // create listener for the connetion + struct sockaddr_in6 *tmpAddr = + parcMemory_AllocateAndClear(sizeof(struct sockaddr_in6)); + + parcAssertNotNull(tmpAddr, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(tmpAddr)); + addressGetInet6(address, tmpAddr); + + char *local_addr = parcMemory_AllocateAndClear(INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, &(tmpAddr->sin6_addr), local_addr, INET6_ADDRSTRLEN); + + parcMemory_Deallocate((void **)&tmpAddr); + + hicn->hicn_fd = hicn_socket(hicnSocketHelper, symbolic, local_addr); + + parcMemory_Deallocate((void **)&local_addr); + } + + if (hicn->hicn_fd < 0) { + if (logger_IsLoggable(hicn->logger, LoggerFacility_IO, + PARCLogLevel_Debug)) { + logger_Log( + hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "HIcnListener %s: error while creating an hicn listener in lib_hicn", + symbolic); + } + logger_Release(&hicn->logger); + addressDestroy(&hicn->localAddress); + parcMemory_Deallocate((void **)&hicn); + return NULL; + } + + // Set non-blocking flag + int flags = fcntl(hicn->hicn_fd, F_GETFL, NULL); + parcAssertTrue(flags != -1, + "fcntl failed to obtain file descriptor flags (%d)", errno); + int failure = fcntl(hicn->hicn_fd, F_SETFL, flags | O_NONBLOCK); + parcAssertFalse(failure, "fcntl failed to set file descriptor flags (%d)", + errno); + + hicn->hicn_event = dispatcher_CreateNetworkEvent( + forwarder_GetDispatcher(forwarder), true, _hicnListener_readcb, + (void *)hicn, hicn->hicn_fd); + dispatcher_StartNetworkEvent(forwarder_GetDispatcher(forwarder), + hicn->hicn_event); + + ListenerOps *ops = parcMemory_AllocateAndClear(sizeof(ListenerOps)); + parcAssertNotNull(ops, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ListenerOps)); + + memcpy(ops, &_hicnTemplate, sizeof(ListenerOps)); + ops->context = hicn; + + if (logger_IsLoggable(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + logger_Log(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "HIcnListener %s created", symbolic); + } + + return ops; +} + +bool _hicnListener_BindInet6(ListenerOps *ops, const Address *remoteAddress) { + HIcnListener *hicn = (HIcnListener *)ops->context; + hicn_socket_helper_t *hicnSocketHelper = + forwarder_GetHIcnSocketHelper(hicn->forwarder); + + struct sockaddr_in6 *tmpAddr = + parcMemory_AllocateAndClear(sizeof(struct sockaddr_in6)); + parcAssertNotNull(tmpAddr, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(tmpAddr)); + addressGetInet6(remoteAddress, tmpAddr); + char *remote_addr = parcMemory_AllocateAndClear(INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, &(tmpAddr->sin6_addr), remote_addr, INET6_ADDRSTRLEN); + parcMemory_Deallocate((void **)&tmpAddr); + + int res = hicn_bind(hicnSocketHelper, hicn->hicn_fd, remote_addr); + + bool result = false; + if (res < 0) { + if (logger_IsLoggable(hicn->logger, LoggerFacility_IO, + PARCLogLevel_Debug)) { + logger_Log(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "hicn_bild failed %d %s", res, hicn_socket_strerror(res)); + } + } else { + result = true; + } + + parcMemory_Deallocate((void **)&remote_addr); + + return result; +} + +bool _hicnListener_BindInet(ListenerOps *ops, const Address *remoteAddress) { + HIcnListener *hicn = (HIcnListener *)ops->context; + hicn_socket_helper_t *hicnSocketHelper = + forwarder_GetHIcnSocketHelper(hicn->forwarder); + + struct sockaddr_in *tmpAddr = + parcMemory_AllocateAndClear(sizeof(struct sockaddr_in)); + parcAssertNotNull(tmpAddr, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(tmpAddr)); + addressGetInet(remoteAddress, tmpAddr); + char *remote_addr = parcMemory_AllocateAndClear(INET_ADDRSTRLEN); + inet_ntop(AF_INET, &(tmpAddr->sin_addr), remote_addr, INET_ADDRSTRLEN); + parcMemory_Deallocate((void **)&tmpAddr); + + int res = hicn_bind(hicnSocketHelper, hicn->hicn_fd, remote_addr); + bool result = false; + + if (res < 0) { + if (logger_IsLoggable(hicn->logger, LoggerFacility_IO, + PARCLogLevel_Debug)) { + logger_Log(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "hicn_bild failed %d %s", res, hicn_socket_strerror(res)); + } + } else { + result = true; + } + + parcMemory_Deallocate((void **)&remote_addr); + + return result; +} + +bool hicnListener_Bind(ListenerOps *ops, const Address *remoteAddress) { + if (addressGetType(remoteAddress) == ADDR_INET) { + return _hicnListener_BindInet(ops, remoteAddress); + } else if (addressGetType(remoteAddress) == ADDR_INET6) { + return _hicnListener_BindInet6(ops, remoteAddress); + } else { + printf("Bind failed: Invalid address\n"); + return false; + } +} + +bool hicnListener_Punting(ListenerOps *ops, const char *prefix) { + HIcnListener *hicn = (HIcnListener *)ops->context; + hicn_socket_helper_t *hicnSocketHelper = + forwarder_GetHIcnSocketHelper(hicn->forwarder); + + int res = hicn_listen(hicnSocketHelper, hicn->hicn_fd, prefix); + int retry = 0; + + while (res < 0 && retry < MAX_HICN_RETRY) { + sleep(1); + res = hicn_listen(hicnSocketHelper, hicn->hicn_fd, prefix); + retry++; + } + + if (res < 0) { + if (logger_IsLoggable(hicn->logger, LoggerFacility_IO, + PARCLogLevel_Debug)) { + logger_Log(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "hicn_listen failed %d %s", res, hicn_socket_strerror(res)); + } + return false; + } + + return true; +} + +bool hicnListener_SetConnectionId(ListenerOps *ops, unsigned connId) { + HIcnListener *hicn = (HIcnListener *)ops->context; + if (hicn) { + hicn->connection_id = connId; + return true; + } + return false; +} + +static void _hicnListener_Destroy(HIcnListener **listenerPtr) { + parcAssertNotNull(listenerPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*listenerPtr, + "Parameter must derefernce to non-null pointer"); + + HIcnListener *hicn = *listenerPtr; + + // close(hicn->hicn_fd); //XXX close the fd in the hicnlib (detroy listener?) + dispatcher_DestroyNetworkEvent(forwarder_GetDispatcher(hicn->forwarder), + &hicn->hicn_event); + logger_Release(&hicn->logger); + addressDestroy(&hicn->localAddress); + parcMemory_Deallocate((void **)&hicn); + *listenerPtr = NULL; +} + +static void _destroy(ListenerOps **listenerOpsPtr) { + ListenerOps *ops = *listenerOpsPtr; + HIcnListener *hicn = (HIcnListener *)ops->context; + _hicnListener_Destroy(&hicn); + parcMemory_Deallocate((void **)&ops); + *listenerOpsPtr = NULL; +} + +static unsigned _getInterfaceIndex(const ListenerOps *ops) { + HIcnListener *hicn = (HIcnListener *)ops->context; + return hicn->conn_id; +} + +static const Address *_getListenAddress(const ListenerOps *ops) { + HIcnListener *hicn = (HIcnListener *)ops->context; + return hicn->localAddress; +} + +static EncapType _getEncapType(const ListenerOps *ops) { return ENCAP_HICN; } + +static int _getSocket(const ListenerOps *ops) { + HIcnListener *hicn = (HIcnListener *)ops->context; + return hicn->hicn_fd; +} + +// =============================== + +static void _readFrameToDiscard(HIcnListener *hicn, int fd) { + // we need to discard the frame. Read 1 byte. This will clear it off the + // stack. + uint8_t buffer; + int nread = read(fd, &buffer, 1); + + if (nread > 0) { + if (logger_IsLoggable(hicn->logger, LoggerFacility_IO, + PARCLogLevel_Debug)) { + logger_Log(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "Discarded frame from fd %d", fd); + } + } else if (nread < 0) { + printf("Error trying to discard frame from fd %d: (%d) %s", fd, errno, + strerror(errno)); + if (logger_IsLoggable(hicn->logger, LoggerFacility_IO, + PARCLogLevel_Error)) { + logger_Log(hicn->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "Error trying to discard frame from fd %d: (%d) %s", fd, errno, + strerror(errno)); + } + } +} + +static unsigned _createNewConnection(HIcnListener *hicn, int fd, + const AddressPair *pair) { + bool isLocal = false; + + // udpConnection_Create takes ownership of the pair + IoOperations *ops = hicnConnection_Create(hicn->forwarder, fd, pair, isLocal); + Connection *conn = connection_Create(ops); + + connectionTable_Add(forwarder_GetConnectionTable(hicn->forwarder), conn); + unsigned connid = ioOperations_GetConnectionId(ops); + + return connid; +} + +const Connection *_findConnectionFromPacket(HIcnListener *hicn, + Address *packetSourceAddress) { + const Connection *conn = NULL; + if (hicn->connection_id != -1) { + conn = connectionTable_FindById( + forwarder_GetConnectionTable(hicn->forwarder), hicn->connection_id); + } else { + if (packetSourceAddress != NULL) { + // in this first check we try to retrieve the standard connection + // generated by the hicn-light + AddressPair *pair = + addressPair_Create(hicn->localAddress, packetSourceAddress); + conn = connectionTable_FindByAddressPair( + forwarder_GetConnectionTable(hicn->forwarder), pair); + addressPair_Release(&pair); + } + } + + return conn; +} + +static Address *_createAddressFromPacket(uint8_t *msgBuffer) { + Address *packetAddr = NULL; + if (messageHandler_GetIPPacketType(msgBuffer) == IPv6_TYPE) { + struct sockaddr_in6 addr_in6; + addr_in6.sin6_family = AF_INET6; + addr_in6.sin6_port = htons(1234); + addr_in6.sin6_flowinfo = 0; + addr_in6.sin6_scope_id = 0; + memcpy(&addr_in6.sin6_addr, + (struct in6_addr *)messageHandler_GetSource(msgBuffer), 16); + packetAddr = addressCreateFromInet6(&addr_in6); + } else if (messageHandler_GetIPPacketType(msgBuffer) == IPv4_TYPE) { + struct sockaddr_in addr_in; + addr_in.sin_family = AF_INET; + addr_in.sin_port = htons(1234); + memcpy(&addr_in.sin_addr, + (struct in_addr *)messageHandler_GetSource(msgBuffer), 4); + packetAddr = addressCreateFromInet(&addr_in); + } + return packetAddr; +} + +static void _handleProbeMessage(HIcnListener *hicn, uint8_t *msgBuffer) { + Address *packetAddr = _createAddressFromPacket(msgBuffer); + + if (packetAddr != NULL) { + const Connection *conn = _findConnectionFromPacket(hicn, packetAddr); + if (conn != NULL) { + // we drop all the probes for a connection that does not exists + connection_HandleProbe((Connection *)conn, msgBuffer, + forwarder_GetTicks(hicn->forwarder)); + } + } + + addressDestroy(&packetAddr); + parcMemory_Deallocate((void **)&msgBuffer); +} + +static void _handleWldrNotification(HIcnListener *hicn, uint8_t *msgBuffer) { + Address *packetAddr = _createAddressFromPacket(msgBuffer); + + if (packetAddr == NULL) { + parcMemory_Deallocate((void **)&msgBuffer); + return; + } + + const Connection *conn = _findConnectionFromPacket(hicn, packetAddr); + if (conn == NULL) { + addressDestroy(&packetAddr); + return; + } + + addressDestroy(&packetAddr); + + Message *message = message_CreateFromByteArray( + connection_GetConnectionId(conn), msgBuffer, + MessagePacketType_WldrNotification, forwarder_GetTicks(hicn->forwarder), + forwarder_GetLogger(hicn->forwarder)); + + connection_HandleWldrNotification((Connection *)conn, message); + + message_Release(&message); +} + +#ifdef WITH_MAPME +static void _handleMapMe(HIcnListener *hicn, int fd, uint8_t *msgBuffer) { + Address *packetAddr = _createAddressFromPacket(msgBuffer); + + if (packetAddr == NULL) { + parcMemory_Deallocate((void **)&msgBuffer); + return; + } + + const Connection *conn = _findConnectionFromPacket(hicn, packetAddr); + unsigned conn_id; + if (conn == NULL) { + /* Unlike the interest path, we don't create virtual connections bound + * on the listener, whose only interest is to send data, but full + * tunnels to be able to route interests + * + * packetAddr is the remote address, we need to ask the lib for our + * local address + * hicn->localAddress is None as the interest is received by the main + * listener. + */ + printf("MapMe, connection did not exist, creating\n"); + + /* Populate remote_address through packetAddr */ + struct sockaddr_in6 sockaddr; // XXX IPv6 only + addressGetInet6(packetAddr, &sockaddr); + ip_address_t remote_address = {.family = AF_INET6, + .prefix_len = IPV6_ADDR_LEN_BITS}; + memcpy(&remote_address.buffer, &sockaddr.sin6_addr, + ip_address_len(&remote_address)); + + /* Get local address through libhicn */ + ip_address_t local_address; + int rc = hicn_get_local_address(&remote_address, &local_address); + if (rc < 0) { + printf("Error getting local address. Discarded mapme packet.\n"); + return; + } + + struct sockaddr_in6 addr_in6; + addr_in6.sin6_family = AF_INET6; + addr_in6.sin6_port = htons(1234); + addr_in6.sin6_flowinfo = 0; + addr_in6.sin6_scope_id = 0; + memcpy(&addr_in6.sin6_addr, (struct in6_addr *)&(local_address.buffer), 16); + + Address *localAddr = addressCreateFromInet6(&addr_in6); + IoOperations *ops = + hicnTunnel_Create(hicn->forwarder, localAddr, packetAddr); + + if (!ops) { + printf("Error creating tunnel. Discarded mapme packet.\n"); + return; + } + + conn = connection_Create(ops); + + connectionTable_Add(forwarder_GetConnectionTable(hicn->forwarder), + (Connection *)conn); + } + conn_id = connection_GetConnectionId(conn); + + addressDestroy(&packetAddr); + + forwarder_ProcessMapMe(hicn->forwarder, msgBuffer, conn_id); +} +#endif /* WITH_MAPME */ + +static Message *_readMessage(HIcnListener *hicn, int fd, uint8_t *msgBuffer) { + Message *message = NULL; + + ssize_t readLength = read(fd, msgBuffer, MTU_SIZE); + + if (readLength < 0) { + printf("read failed %d: (%d) %s\n", fd, errno, strerror(errno)); + return message; + } + + size_t packetLength = messageHandler_GetTotalPacketLength(msgBuffer); + + if (readLength != packetLength) { + parcMemory_Deallocate((void **)&msgBuffer); + return message; + } + + if (messageHandler_IsTCP(msgBuffer)) { + MessagePacketType pktType; + unsigned connid = 0; + if (messageHandler_IsData(msgBuffer)) { + pktType = MessagePacketType_ContentObject; + if (hicn->connection_id == -1) { + parcMemory_Deallocate((void **)&msgBuffer); + return message; + } else { + connid = hicn->connection_id; + } + } else if (messageHandler_IsInterest(msgBuffer)) { + // notice that the connections for the interest (the one that we create at + // run time) uses as a local address 0::0, so the main tun + pktType = MessagePacketType_Interest; + Address *packetAddr = _createAddressFromPacket(msgBuffer); + const Connection *conn = _findConnectionFromPacket(hicn, packetAddr); + + if (conn == NULL) { + AddressPair *pair = addressPair_Create(hicn->localAddress, packetAddr); + connid = _createNewConnection(hicn, fd, pair); + addressPair_Release(&pair); + } else { + connid = connection_GetConnectionId(conn); + } + addressDestroy(&packetAddr); + } else { + printf("Got a packet that is not a data nor an interest, drop it!\n"); + parcMemory_Deallocate((void **)&msgBuffer); + return message; + } + + message = message_CreateFromByteArray(connid, msgBuffer, pktType, + forwarder_GetTicks(hicn->forwarder), + forwarder_GetLogger(hicn->forwarder)); + if (message == NULL) { + parcMemory_Deallocate((void **)&msgBuffer); + } + } else if (messageHandler_IsWldrNotification(msgBuffer)) { + _handleWldrNotification(hicn, msgBuffer); + } else if (messageHandler_IsLoadBalancerProbe(msgBuffer)) { + _handleProbeMessage(hicn, msgBuffer); + } +#ifdef WITH_MAPME + else if (mapMe_isMapMe(msgBuffer)) { + /* This function triggers the handling of the MAP-Me message, and we + * will return NULL so as to terminate the processing of this + * msgBuffer. */ + _handleMapMe(hicn, fd, msgBuffer); + } +#endif /* WITH_MAPME */ + + return message; +} + +static void _receivePacket(HIcnListener *hicn, int fd) { + Message *msg = NULL; + uint8_t *msgBuffer = parcMemory_AllocateAndClear(MTU_SIZE); + msg = _readMessage(hicn, fd, msgBuffer); + + if (msg) { + forwarder_Receive(hicn->forwarder, msg); + } +} + +static void _hicnListener_readcb(int fd, PARCEventType what, void *hicnVoid) { + HIcnListener *hicn = (HIcnListener *)hicnVoid; + + if (hicn->inetFamily == IPv4 || hicn->inetFamily == IPv6) { + if (what & PARCEventType_Read) { + _receivePacket(hicn, fd); + } + } else { + _readFrameToDiscard(hicn, fd); + } +} diff --git a/hicn-light/src/io/hicnListener.h b/hicn-light/src/io/hicnListener.h new file mode 100755 index 000000000..98c5387c9 --- /dev/null +++ b/hicn-light/src/io/hicnListener.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file hicnListener.h + * @brief Listens for in coming HIcn connections + * + * + */ + +#ifndef hicnListener_h +#define hicnListener_h + +#include +#include +#include +#include + +struct hicn_listener; +typedef struct hicn_listener HIcnListener; + +ListenerOps *hicnListener_CreateInet(Forwarder *forwarder, char *symbolic, + Address *address); +ListenerOps *hicnListener_CreateInet6(Forwarder *forwarder, char *symbolic, + Address *address); +bool hicnListener_Punting(ListenerOps *ops, const char *prefix); +bool hicnListener_Bind(ListenerOps *ops, const Address *remoteAddress); +bool hicnListener_SetConnectionId(ListenerOps *ops, unsigned connId); +// const Address *hicnListener_GetTunAddress(const ListenerOps *ops); +#endif // hicnListener_h diff --git a/hicn-light/src/io/hicnTunnel.c b/hicn-light/src/io/hicnTunnel.c new file mode 100755 index 000000000..e55393137 --- /dev/null +++ b/hicn-light/src/io/hicnTunnel.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +IoOperations *hicnTunnel_CreateOnListener(Forwarder *forwarder, + ListenerOps *localListener, + const Address *remoteAddress) { + parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null"); + parcAssertNotNull(localListener, "Parameter localListener must be non-null"); + parcAssertNotNull(remoteAddress, "Parameter remoteAddress must be non-null"); + + Logger *logger = forwarder_GetLogger(forwarder); + + IoOperations *ops = NULL; + if (localListener->getEncapType(localListener) == ENCAP_HICN) { + const Address *localAddress = + localListener->getListenAddress(localListener); + address_type localType = addressGetType(localAddress); + address_type remoteType = addressGetType(remoteAddress); + + if (localType == remoteType) { + bool res = hicnListener_Bind(localListener, remoteAddress); + if (res == false) { + if (logger_IsLoggable(logger, LoggerFacility_IO, PARCLogLevel_Error)) { + logger_Log(logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "Unable to bind local listener to remote node"); + } + return ops; + } + + // localAddress = hicnListener_GetTunAddress(localListener); //This is the + // true local address + + AddressPair *pair = addressPair_Create(localAddress, remoteAddress); + bool isLocal = false; + int fd = localListener->getSocket(localListener); + ops = hicnConnection_Create(forwarder, fd, pair, isLocal); + + addressPair_Release(&pair); + } else { + if (logger_IsLoggable(logger, LoggerFacility_IO, PARCLogLevel_Error)) { + logger_Log(logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "Local listener of type %s and remote type %s, cannot " + "establish tunnel", + addressTypeToString(localType), + addressTypeToString(remoteType)); + } + } + } else { + if (logger_IsLoggable(logger, LoggerFacility_IO, PARCLogLevel_Error)) { + logger_Log(logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "Local listener %p is not type UDP, cannot establish tunnel", + (void *)localListener); + } + } + + return ops; +} + +IoOperations *hicnTunnel_Create(Forwarder *forwarder, + const Address *localAddress, + const Address *remoteAddress) { + ListenerSet *set = forwarder_GetListenerSet(forwarder); + ListenerOps *listener = listenerSet_Find(set, ENCAP_HICN, localAddress); + IoOperations *ops = NULL; + if (listener) { + ops = hicnTunnel_CreateOnListener(forwarder, listener, remoteAddress); + } else { + if (logger_IsLoggable(forwarder_GetLogger(forwarder), LoggerFacility_IO, + PARCLogLevel_Error)) { + char *str = addressToString(localAddress); + logger_Log(forwarder_GetLogger(forwarder), LoggerFacility_IO, + PARCLogLevel_Error, __func__, + "Could not find listener to match address %s", str); + parcMemory_Deallocate((void **)&str); + } + } + + if (ops) { + hicnListener_SetConnectionId(listener, ops->getConnectionId(ops)); + } + + return ops; +} diff --git a/hicn-light/src/io/hicnTunnel.h b/hicn-light/src/io/hicnTunnel.h new file mode 100755 index 000000000..70295797c --- /dev/null +++ b/hicn-light/src/io/hicnTunnel.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file hicnTunnel.h + * @brief Establish a tunnel to a remote system + * + * Creates a "hicn tunnel" to a remote system. There must already be a local + * HICN listener for the local side of the connection. + * + */ + +#ifndef hicnTunnel_h +#define hicnTunnel_h + +#include +#include +#include +#include + +/** + * Establishes a connection to a remote system over HICN + * + * The remoteAddress must be of the same type (i.e. v4 or v6) as the + * localAddress. There must be an existing HICN listener on the local address. + * If either of these are not true, will return NULL. + * + * The connection will go in the table immediately, and will be in the "up" + * state. + * + * @param [in] an allocated hicn-light Forwarder + * @param [in] localAddress The local IP address and port to use for the + * connection + * @param [in] remote Address the remote IP address for the connection, must + * include a destination port. + * + * @retval non-null An allocated Io Operations structure for the connection + * @retval null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +IoOperations *hicnTunnel_Create(Forwarder *forwarder, + const Address *localAddress, + const Address *remoteAddress); + +IoOperations *hicnTunnel_CreateOnListener(Forwarder *forwarder, + ListenerOps *localListener, + const Address *remoteAddress); + +#endif // hicnTunnel_h diff --git a/hicn-light/src/io/ioOperations.c b/hicn-light/src/io/ioOperations.c new file mode 100755 index 000000000..bbc8cec91 --- /dev/null +++ b/hicn-light/src/io/ioOperations.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +void *ioOperations_GetClosure(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter ops must be non-null"); + return ops->closure; +} + +bool ioOperations_Send(IoOperations *ops, const Address *nexthop, + Message *message) { + return ops->send(ops, nexthop, message); +} + +const Address *ioOperations_GetRemoteAddress(const IoOperations *ops) { + return ops->getRemoteAddress(ops); +} + +const AddressPair *ioOperations_GetAddressPair(const IoOperations *ops) { + return ops->getAddressPair(ops); +} + +bool ioOperations_IsUp(const IoOperations *ops) { return ops->isUp(ops); } + +bool ioOperations_IsLocal(const IoOperations *ops) { return ops->isLocal(ops); } + +unsigned ioOperations_GetConnectionId(const IoOperations *ops) { + return ops->getConnectionId(ops); +} + +void ioOperations_Release(IoOperations **opsPtr) { + IoOperations *ops = *opsPtr; + ops->destroy(opsPtr); +} + +const void *ioOperations_Class(const IoOperations *ops) { + return ops->class(ops); +} + +list_connections_type ioOperations_GetConnectionType(const IoOperations *ops) { + return ops->getConnectionType(ops); +} + +Ticks ioOperations_SendProbe(IoOperations *ops, unsigned probeType, + uint8_t *message) { + return ops->sendProbe(ops, probeType, message); +} diff --git a/hicn-light/src/io/ioOperations.h b/hicn-light/src/io/ioOperations.h new file mode 100755 index 000000000..dee66030d --- /dev/null +++ b/hicn-light/src/io/ioOperations.h @@ -0,0 +1,394 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Defines the interface all connections use to communicate with the forwarder. + * + * @code + * + * static IoOperations _template = { + * .closure = NULL, + * .send = &_etherConnection_Send, + * .getRemoteAddress = &_etherConnection_GetRemoteAddress, + * .getAddressPair = &_etherConnection_GetAddressPair, + * .getConnectionId = &_etherConnection_GetConnectionId, + * .isUp = &_etherConnection_IsUp, + * .isLocal = &_etherConnection_IsLocal, + * .destroy = &_etherConnection_DestroyOperations, + * .class = &_etherConnection_Class, + * .getConnectionType = &_etherConnection_getConnectionType + * }; + * + * IoOperations * + * etherConnection_Create(Forwarder *forwarder, GenericEther *ether, + * AddressPair *pair) + * { + * _EtherState *etherConnState = parcMemory_Allocate(sizeof(_EtherState)); + * // Fill in etherConnState with instance variables + * + * IoOperations *io_ops = parcMemory_Allocate(sizeof(IoOperations)); + * memcpy(io_ops, &_template, sizeof(IoOperations)); + * io_ops->closure = etherConnState; + * // Add to connection table, send missives about connection state + * + * return op_ops; + * } + * @endcode + * + */ + +/** + * I/O is built around a callback structure. The connection table contains an + * operations structure built around function pointers. These allow the + * connection table to be agnostic about underlying connections. + */ + +#ifndef io_h +#define io_h + +#include +#include +#include +#include + +// packet types for probing +#define PACKET_TYPE_PROBE_REQUEST 5 +#define PACKET_TYPE_PROBE_REPLY 6 + +struct io_ops; +typedef struct io_ops IoOperations; + +/** + * @typedef IoOperations + * @abstract The IO Operations structure abstracts an connection's properties + * and send() method + * @constant context Implementation specific opaque data, passed back on each + * call + * @constant send function pointer to send a message, does not destroy the + * message + * @constant getRemoteAddress function pointer to return the "to" address + * associated with the connection. Some connections might not have a specific + * peer, such as multicast, where its the group address. + * @constant isUp test if the connection is up, ready to send a message. + * @constant isLocal test if the connection is local to the host. + * @constant getConnectionId returns the hicn-light id for the connection. + * @constant destroy releases a refernce count on the connection and possibly + * destroys the connection. + * @constant class A unique identifier for each class that instantiates + * IoOperations. + * @constant getConnectionType Returns the type of connection (TCP, UDP, L2, + * etc.) of the underlying connection. + * @discussion <#Discussion#> + */ +struct io_ops { + void *closure; + bool (*send)(IoOperations *ops, const Address *nexthop, Message *message); + const Address *(*getRemoteAddress)(const IoOperations *ops); + const AddressPair *(*getAddressPair)(const IoOperations *ops); + bool (*isUp)(const IoOperations *ops); + bool (*isLocal)(const IoOperations *ops); + unsigned (*getConnectionId)(const IoOperations *ops); + void (*destroy)(IoOperations **opsPtr); + const void *(*class)(const IoOperations *ops); + list_connections_type (*getConnectionType)(const IoOperations *ops); + Ticks (*sendProbe)(IoOperations *ops, unsigned probeType, uint8_t *message); +}; + +/** + * Returns the closure of the interface + * + * The creator of the closure sets this parameter to store its state. + * + * @param [in] ops A concrete instance of the interface + * + * @return The value set by the concrete instance of the interface. + * + * Example: + * @clode + * { + + * } + * @endcode + */ +void *ioOperations_GetClosure(const IoOperations *ops); + +/** + * Release all memory related to the interface and implementation + * + * This function must release all referenced memory in the concrete + * implementation and memory related to the IoOperations. It should NULL the + * input parameter. + * + * @param [in,out] opsPtr Pointer to interface. Will be NULLed. + * + * Example: + * @code + * + * static void + * _etherConnection_InternalRelease(_EtherState *etherConnState) + * { + * // release internal state of _EtherState + * } + * + * static void + * _etherConnection_Release(IoOperations **opsPtr) + * { + * IoOperations *ops = *opsPtr; + * + * _EtherState *etherConnState = (_EtherState *) + * ioOperations_GetClosure(ops); + * _etherConnection_InternalRelease(etherConnState); + * + * parcMemory_Deallocate((void **) &ops); + * } + * + * IoOperations * + * etherConnection_Create(Forwarder *forwarder, GenericEther *ether, + * AddressPair *pair) + * { + * size_t allocationSize = sizeof(_EtherState) + sizeof(IoOperations); + * IoOperations *ops = parcMemory_AllocateAndClear(allocationSize); + * if (ops) { + * // fill in other interface functions + * ops->destroy = &_etherConnection_Release; + * ops->closure = (uint8_t *) ops + sizeof(IoOperations); + * + * _EtherState *etherConnState = ioOperations_GetClosure(ops); + * // fill in Ethernet state + * } + * return ops; + * } + * @endcode + */ +void ioOperations_Release(IoOperations **opsPtr); + +/** + * Sends the specified Message out this connection + * + * The the implementation of send may queue the message, it must acquire a + * reference to it. + * + * @param [in] ops The connection implementation. + * @param [in] nexthop On multiple access networks, this parameter might be + * used, usually NULL. + * @param [in] message The message to send. If the message will be queued, it + * will be acquired. + * + * @return true The message was sent or queued + * @retrun false An error occured and the message will not be sent or queued + * + * Example: + * @code + * { + * if (ioOperations_IsUp(conn->ops)) { + * return ioOperations_Send(conn->ops, NULL, message); + * } + * } + * @endcode + */ +bool ioOperations_Send(IoOperations *ops, const Address *nexthop, + Message *message); + +/** + * A connection is made up of a local and a remote address. This function + * returns the remote address. + * + * Represents the destination endpoint of the communication. + * + * @param [in] ops The connection implementation. + * + * @return non-null The remote address + * @return null The connection does not have a remote address + * + * Example: + * @code + * { + * Address *local = addressCreateFromLink((uint8_t []) { 0x01, 0x02, 0x03, + * 0x04, 0x05, 0x06 }, 6); Address *remote = addressCreateFromLink((uint8_t []) + * { 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F }, 6); AddressPair *pair = + * addressPair_Create(local, remote); IoOperations *ops = + * etherConnection_Create(forwarder, ether, pair); + * + * const Address *test = ioOperations_GetRemoteAddress(ops); + * parcAssertTrue(addressEquals(test, remote), "Wrong remote address"); + * ioOperations_Release(&ops); + * addressPair_Release(&pair); + * addressDestroy(&local); + * addressDestroy(&remote); + * } + * @endcode + */ +const Address *ioOperations_GetRemoteAddress(const IoOperations *ops); + +/** + * A connection is made up of a local and a remote address. This function + * returns the address pair. + * + * Represents the destination endpoint of the communication. + * + * @param [in] ops The connection implementation. + * + * @return non-null The address pair + * @return null An error. + * + * Example: + * @code + * { + * Address *local = addressCreateFromLink((uint8_t []) { 0x01, 0x02, 0x03, + * 0x04, 0x05, 0x06 }, 6); Address *remote = addressCreateFromLink((uint8_t []) + * { 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F }, 6); AddressPair *pair = + * addressPair_Create(local, remote); IoOperations *ops = + * etherConnection_Create(forwarder, ether, pair); + * + * const AddressPair *test = ioOperations_GetAddressPair(ops); + * parcAssertTrue(addressPair(test, pair), "Wrong address pair"); + * ioOperations_Release(&ops); + * addressPair_Release(&pair); + * addressDestroy(&local); + * addressDestroy(&remote); + * } + * @endcode + */ +const AddressPair *ioOperations_GetAddressPair(const IoOperations *ops); + +/** + * Returns true if the underlying connection is in operation + * + * An UP connection is able to send and receive packets. If a subsystem needs to + * take actions when a connection goes UP or DOWN, it should subscribe as a + * Missive listener. + * + * @param [in] ops The connection implementation. + * + * @return true The connection is UP + * @return false The connection is not UP + * + * Example: + * @code + * { + * if (ioOperations_IsUp(conn->ops)) { + * return ioOperations_Send(conn->ops, NULL, message); + * } + * } + * @endcode + */ +bool ioOperations_IsUp(const IoOperations *ops); + +/** + * If the remote address is local to this system, returns true + * + * Will return true if an INET or INET6 connection is on localhost. Will return + * true for AF_UNIX. An Ethernet connection is not local. + * + * @param [in] ops The connection implementation. + * + * @return true The remote address is local to the system + * @return false The remote address is not local + * + * Example: + * @code + * { + * // Is the ingress connection remote? If so check for non-zero and + * decrement if (!ioOperations(ingressConnectionOps) { uint8_t hoplimit = + * message_GetHopLimit(interestMessage); if (hoplimit == 0) { + * // error + * } else { + * hoplimit--; + * } + * // take actions on hoplimit + * } + * } + * @endcode + */ +bool ioOperations_IsLocal(const IoOperations *ops); + +/** + * Returns the connection ID represented by this IoOperations in the + * ConnectionTable. + * + * <#Paragraphs Of Explanation#> + * + * @param [in] ops The connection implementation. + * + * @return number The connection ID in the connection table. + * + * Example: + * @code + * { + * unsigned id = ioOperations_GetConnectionId(ingressIoOps); + * const Connection *conn = + * connectionTable_FindById(forwarder->connectionTable, id); + * } + * @endcode + */ +unsigned ioOperations_GetConnectionId(const IoOperations *ops); + +/** + * A pointer that represents the class of the connection + * + * Each concrete implementation has a class pointer that is unique to the + * implementation (not instance). Each implementation is free to choose how to + * determine the value, so long as it is unique on the system. This is a + * system-local value. + * + * @param [in] ops The connection implementation. + * + * @return non-null A pointer value unique to the implementation (not instance). + * + * Example: + * @code + * bool + * etherConnection_IsInstanceOf(const Connection *conn) + * { + * bool result = false; + * if (conn != NULL) { + * IoOperations *ops = connection_GetIoOperations(conn); + * const void *class = ioOperations_Class(ops); + * result = (class == _etherConnection_Class(ops)); + * } + * return result; + * } + * @endcode + */ +const void *ioOperations_Class(const IoOperations *ops); + +/** + * Returns the transport type of the connection (TCP, UDP, L2, etc.). + * + * TCP and AF_UNIX are both stream connections and will both return + * "Connection_TCP". Ethernet will return "Connection_L2". + * + * @param [in] ops The connection implementation. + * + * @return Connection_TCP A TCP4, TCP6, or AF_UNIX connection + * @return Connection_UDP A UDP4 or UDP6 connection + * @return Connection_L2 An Ethernet connection + * + * Example: + * @code + * { + * ConnectionType type = + * ioOperations_GetConnectionType(connection_GetIoOperations(connection)); + * Connection *Conn = + * Connection_Create(connection_GetConnectionId(connection), localAddress, + * remoteAddress, type); + * } + * @endcode + */ +list_connections_type ioOperations_GetConnectionType(const IoOperations *ops); + +Ticks ioOperations_SendProbe(IoOperations *ops, unsigned probeType, + uint8_t *message); +#endif // io_h diff --git a/hicn-light/src/io/listener.h b/hicn-light/src/io/listener.h new file mode 100755 index 000000000..ffbb513fa --- /dev/null +++ b/hicn-light/src/io/listener.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file listener.h + * @brief Provides the function abstraction of all Listeners. + * + * A listener accepts in coming packets. A Stream listener will accept the + * connection then pass it off to the {@link StreamConnection} class. A + * datagram listener will have to have its own way to multiplex packets. + * + */ + +#ifndef listener_h +#define listener_h + +#include + +struct listener_ops; +typedef struct listener_ops ListenerOps; + +typedef enum { + ENCAP_TCP, /**< TCP encapsulation type */ + ENCAP_UDP, /**< UDP encapsulation type */ + ENCAP_ETHER, /**< Ethernet encapsulation type */ + ENCAP_LOCAL, /**< A connection to a local protocol stack */ + ENCAP_HICN +} EncapType; + +struct listener_ops { + /** + * A user-defined parameter + */ + void *context; + + /** + * Called to destroy the Listener. + * + * @param [in] listenerOpsPtr Double pointer to this structure + */ + void (*destroy)(ListenerOps **listenerOpsPtr); + + /** + * Returns the interface index of the listener. + * + * @param [in] ops Pointer to this structure + * + * @return the interface index of the listener + */ + unsigned (*getInterfaceIndex)(const ListenerOps *ops); + + /** + * Returns the address pair that defines the listener (local, remote) + * + * @param [in] ops Pointer to this structure + * + * @return the (local, remote) pair of addresses + */ + const Address *(*getListenAddress)(const ListenerOps *ops); + + /** + * Returns the encapsulation type of the listener (e.g. TCP, UDP, HICN) + * + * @param [in] ops Pointer to this structure + * + * @return the listener encapsulation type + */ + EncapType (*getEncapType)(const ListenerOps *ops); + + /** + * Returns the underlying socket associated with the listener + * + * Not all listeners are capable of returning a useful socket. In those + * cases, this function pointer is NULL. + * + * TCP does not support this operation (function is NULL). UDP returns its + * local socket. + * + * The caller should never close this socket, the listener will do that when + * its destroy method is called. + * + * @param [in] ops Pointer to this structure + * + * @retval integer The socket descriptor + * + * Example: + * @code + * <#example#> + * @endcode + */ + int (*getSocket)(const ListenerOps *ops); +}; +#endif // listener_h diff --git a/hicn-light/src/io/listenerSet.c b/hicn-light/src/io/listenerSet.c new file mode 100755 index 000000000..a890cd5b8 --- /dev/null +++ b/hicn-light/src/io/listenerSet.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include +#include + +struct listener_set { + PARCArrayList *listOfListeners; +}; + +static void listenerSet_DestroyListenerOps(void **opsPtr) { + ListenerOps *ops = *((ListenerOps **)opsPtr); + ops->destroy(&ops); +} + +ListenerSet *listenerSet_Create() { + ListenerSet *set = parcMemory_AllocateAndClear(sizeof(ListenerSet)); + parcAssertNotNull(set, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ListenerSet)); + set->listOfListeners = parcArrayList_Create(listenerSet_DestroyListenerOps); + + return set; +} + +void listenerSet_Destroy(ListenerSet **setPtr) { + parcAssertNotNull(setPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*setPtr, "Parameter must dereference to non-null pointer"); + + ListenerSet *set = *setPtr; + parcArrayList_Destroy(&set->listOfListeners); + parcMemory_Deallocate((void **)&set); + *setPtr = NULL; +} + +/** + * @function listenerSet_Add + * @abstract Adds the listener to the set + * @discussion + * Unique set based on pair (EncapType, localAddress) + * + * @param <#param1#> + * @return <#return#> + */ +bool listenerSet_Add(ListenerSet *set, ListenerOps *ops) { + parcAssertNotNull(set, "Parameter set must be non-null"); + parcAssertNotNull(ops, "Parameter ops must be non-null"); + + int opsEncap = ops->getEncapType(ops); + const Address *opsAddress = ops->getListenAddress(ops); + + // make sure its not in the set + size_t length = parcArrayList_Size(set->listOfListeners); + for (size_t i = 0; i < length; i++) { + ListenerOps *entry = parcArrayList_Get(set->listOfListeners, i); + + int entryEncap = entry->getEncapType(entry); + const Address *entryAddress = entry->getListenAddress(entry); + + if (opsEncap == entryEncap && addressEquals(opsAddress, entryAddress)) { + // duplicate + return false; + } + } + + parcArrayList_Add(set->listOfListeners, ops); + return true; +} + +size_t listenerSet_Length(const ListenerSet *set) { + parcAssertNotNull(set, "Parameter set must be non-null"); + return parcArrayList_Size(set->listOfListeners); +} + +/** + * Returns the listener at the given index + * + * <#Paragraphs Of Explanation#> + * + * @param [in] set An allocated listener set + * @param [in] index The index position (0 <= index < listenerSet_Count) + * + * @retval non-null The listener at index + * @retval null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +ListenerOps *listenerSet_Get(const ListenerSet *set, size_t index) { + parcAssertNotNull(set, "Parameter set must be non-null"); + return parcArrayList_Get(set->listOfListeners, index); +} + +ListenerOps *listenerSet_Find(const ListenerSet *set, EncapType encapType, + const Address *localAddress) { + parcAssertNotNull(set, "Parameter set must be non-null"); + parcAssertNotNull(localAddress, "Parameter localAddress must be non-null"); + + ListenerOps *match = NULL; + + for (size_t i = 0; i < parcArrayList_Size(set->listOfListeners) && !match; + i++) { + ListenerOps *ops = parcArrayList_Get(set->listOfListeners, i); + parcAssertNotNull(ops, "Got null listener ops at index %zu", i); + + if (ops->getEncapType(ops) == encapType) { + if (addressEquals(localAddress, ops->getListenAddress(ops))) { + match = ops; + } + } + } + + return match; +} diff --git a/hicn-light/src/io/listenerSet.h b/hicn-light/src/io/listenerSet.h new file mode 100755 index 000000000..671e68479 --- /dev/null +++ b/hicn-light/src/io/listenerSet.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file listenerSet.h + * @brief A listener set is unique on (EncapType, localAddress) + * + * Keeps track of all the running listeners. The set is unique on the + * encapsulation type and the local address. For example, with TCP + * encapsulation and local address 127.0.0.1 or Ethernet encapsulation and MAC + * address 00:11:22:33:44:55. + * + * NOTE: This does not allow multiple EtherType on the same interface because + * the Address for a LINK address does not include an EtherType. + * + */ + +#ifndef listenerSet_h +#define listenerSet_h + +#include + +struct listener_set; +typedef struct listener_set ListenerSet; + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @retval <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +ListenerSet *listenerSet_Create(void); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @retval <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void listenerSet_Destroy(ListenerSet **setPtr); + +/** + * @function listenerSet_Add + * @abstract Adds the listener to the set + * @discussion + * Unique set based on pair (EncapType, localAddress). + * Takes ownership of the ops memory if added. + * + * @param <#param1#> + * @return true if added, false if not + */ +bool listenerSet_Add(ListenerSet *set, ListenerOps *ops); + +/** + * The number of listeners in the set + * + * <#Paragraphs Of Explanation#> + * + * @param [in] set An allocated listener set + * + * @retval <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t listenerSet_Length(const ListenerSet *set); +size_t listenerSet_Length(const ListenerSet *set); + +/** + * Returns the listener at the given index + * + * <#Paragraphs Of Explanation#> + * + * @param [in] set An allocated listener set + * @param [in] index The index position (0 <= index < listenerSet_Lenght) + * + * @retval non-null The listener at index + * @retval null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +ListenerOps *listenerSet_Get(const ListenerSet *set, size_t index); + +/** + * Looks up a listener by its key (EncapType, LocalAddress) + * + * <#Paragraphs Of Explanation#> + * + * @param [in] set An allocated listener set + * @param [in] encapType the listener type + * @param [in] localAddress The local bind address (e.g. MAC address or TCP + * socket) + * + * @retval non-null The listener matching the query + * @retval null Does not exist + * + * Example: + * @code + * + * @endcode + */ +ListenerOps *listenerSet_Find(const ListenerSet *set, EncapType encapType, + const Address *localAddress); +#endif diff --git a/hicn-light/src/io/streamConnection.c b/hicn-light/src/io/streamConnection.c new file mode 100755 index 000000000..fedbb157a --- /dev/null +++ b/hicn-light/src/io/streamConnection.c @@ -0,0 +1,714 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Common activity for STREAM based listeners. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +// 128 KB output queue +#define OUTPUT_QUEUE_BYTES (128 * 1024) + +static void _conn_readcb(PARCEventQueue *bufferEventVector, PARCEventType type, + void *ioOpsVoid); + +static void _conn_eventcb(PARCEventQueue *bufferEventVector, + PARCEventQueueEventType events, void *ioOpsVoid); + +typedef struct stream_state { + Forwarder *forwarder; + Logger *logger; + + int fd; + + AddressPair *addressPair; + PARCEventQueue *bufferEventVector; + + bool isLocal; + bool isUp; + bool isClosed; + unsigned id; + + size_t nextMessageLength; +} _StreamState; + +// Prototypes +static bool _streamConnection_Send(IoOperations *ops, const Address *nexthop, + Message *message); +static const Address *_streamConnection_GetRemoteAddress( + const IoOperations *ops); +static const AddressPair *_streamConnection_GetAddressPair( + const IoOperations *ops); +static unsigned _streamConnection_GetConnectionId(const IoOperations *ops); +static bool _streamConnection_IsUp(const IoOperations *ops); +static bool _streamConnection_IsLocal(const IoOperations *ops); +static void _streamConnection_DestroyOperations(IoOperations **opsPtr); + +static void _setConnectionState(_StreamState *stream, bool isUp); +static list_connections_type _streamConnection_GetConnectionType( + const IoOperations *ops); +static Ticks _sendProbe(IoOperations *ops, unsigned probeType, + uint8_t *message); + +// REMINDER: when a new_command is added, the following array has to be updated +// with the sizeof(new_command). It allows to allocate the buffer for receiving +// the payload of the CONTROLLER REQUEST after the header has beed read. Each +// command identifier (typedef enum command_id) corresponds to a position in the +// following array. +static int payloadLengthDaemon[LAST_COMMAND_VALUE] = { + sizeof(add_listener_command), + sizeof(add_connection_command), + 0, // list connections: payload always 0 when get controller request + sizeof(add_route_command), + 0, // list routes: payload always 0 when get controller request + sizeof(remove_connection_command), + sizeof(remove_route_command), + sizeof(cache_store_command), + sizeof(cache_serve_command), + 0, // cache clear + sizeof(set_strategy_command), + sizeof(set_wldr_command), + sizeof(add_punting_command), + 0, // list listeners: payload always 0 when get controller request + sizeof(mapme_activator_command), + sizeof(mapme_activator_command), + sizeof(mapme_timing_command), + sizeof(mapme_timing_command)}; + +/* + * This assigns a unique pointer to the void * which we use + * as a GUID for this class. + */ +static const void *_ioOperationsGuid = __FILE__; + +/* + * Return our GUID + */ +static const void *_streamConnection_Class(const IoOperations *ops) { + return _ioOperationsGuid; +} + +static IoOperations _template = { + .closure = NULL, + .send = &_streamConnection_Send, + .getRemoteAddress = &_streamConnection_GetRemoteAddress, + .getAddressPair = &_streamConnection_GetAddressPair, + .getConnectionId = &_streamConnection_GetConnectionId, + .isUp = &_streamConnection_IsUp, + .isLocal = &_streamConnection_IsLocal, + .destroy = &_streamConnection_DestroyOperations, + .class = &_streamConnection_Class, + .getConnectionType = &_streamConnection_GetConnectionType, + .sendProbe = &_sendProbe, +}; + +IoOperations *streamConnection_AcceptConnection(Forwarder *forwarder, int fd, + AddressPair *pair, + bool isLocal) { + _StreamState *stream = parcMemory_AllocateAndClear(sizeof(_StreamState)); + parcAssertNotNull(stream, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(_StreamState)); + + Dispatcher *dispatcher = forwarder_GetDispatcher(forwarder); + PARCEventScheduler *eventBase = dispatcher_GetEventScheduler(dispatcher); + stream->bufferEventVector = parcEventQueue_Create( + eventBase, fd, + PARCEventQueueOption_CloseOnFree | PARCEventQueueOption_DeferCallbacks); + + stream->forwarder = forwarder; + stream->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + stream->fd = fd; + stream->id = forwarder_GetNextConnectionId(forwarder); + stream->addressPair = pair; + stream->isClosed = false; + + // allocate a connection + IoOperations *io_ops = parcMemory_AllocateAndClear(sizeof(IoOperations)); + parcAssertNotNull(io_ops, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(IoOperations)); + memcpy(io_ops, &_template, sizeof(IoOperations)); + io_ops->closure = stream; + stream->isLocal = isLocal; + + parcEventQueue_SetCallbacks(stream->bufferEventVector, _conn_readcb, NULL, + _conn_eventcb, (void *)io_ops); + parcEventQueue_Enable(stream->bufferEventVector, PARCEventType_Read); + + messenger_Send(forwarder_GetMessenger(stream->forwarder), + missive_Create(MissiveType_ConnectionCreate, stream->id)); + + // As we are acceting a connection, we begin in the UP state + _setConnectionState(stream, true); + + if (logger_IsLoggable(stream->logger, LoggerFacility_IO, + PARCLogLevel_Debug)) { + char *pair_str = addressPair_ToString(pair); + logger_Log(stream->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "StreamConnection %p accept for address pair %s", (void *)stream, + pair_str); + free(pair_str); + } + + return io_ops; +} + +IoOperations *streamConnection_OpenConnection(Forwarder *forwarder, + AddressPair *pair, bool isLocal) { + parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null"); + parcAssertNotNull(pair, "Parameter pair must be non-null"); + + // if there's an error on the bind or connect, will return NULL + PARCEventQueue *bufferEventVector = + dispatcher_StreamBufferConnect(forwarder_GetDispatcher(forwarder), pair); + if (bufferEventVector == NULL) { + // error opening connection + return NULL; + } + + _StreamState *stream = parcMemory_AllocateAndClear(sizeof(_StreamState)); + parcAssertNotNull(stream, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(_StreamState)); + + stream->forwarder = forwarder; + stream->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + stream->fd = parcEventQueue_GetFileDescriptor(bufferEventVector); + stream->bufferEventVector = bufferEventVector; + stream->id = forwarder_GetNextConnectionId(forwarder); + stream->addressPair = pair; + stream->isClosed = false; + + // allocate a connection + IoOperations *io_ops = parcMemory_AllocateAndClear(sizeof(IoOperations)); + parcAssertNotNull(io_ops, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(IoOperations)); + memcpy(io_ops, &_template, sizeof(IoOperations)); + io_ops->closure = stream; + stream->isLocal = isLocal; + + parcEventQueue_SetCallbacks(stream->bufferEventVector, _conn_readcb, NULL, + _conn_eventcb, (void *)io_ops); + parcEventQueue_Enable(stream->bufferEventVector, PARCEventType_Read); + + // we start in DOWN state, until remote side answers + messenger_Send(forwarder_GetMessenger(stream->forwarder), + missive_Create(MissiveType_ConnectionCreate, stream->id)); + _setConnectionState(stream, false); + + if (logger_IsLoggable(stream->logger, LoggerFacility_IO, PARCLogLevel_Info)) { + char *pair_str = addressPair_ToString(pair); + logger_Log(stream->logger, LoggerFacility_IO, PARCLogLevel_Info, __func__, + "StreamConnection %p connect for address pair %s", + (void *)stream, pair_str); + free(pair_str); + } + + return io_ops; +} + +static void _streamConnection_DestroyOperations(IoOperations **opsPtr) { + parcAssertNotNull(opsPtr, "Parameter opsPtr must be non-null double pointer"); + parcAssertNotNull(*opsPtr, + "Parameter opsPtr must dereference to non-null pointer"); + + IoOperations *ops = *opsPtr; + parcAssertNotNull(ioOperations_GetClosure(ops), + "ops->context must not be null"); + + _StreamState *stream = (_StreamState *)ioOperations_GetClosure(ops); + + parcEventQueue_Destroy(&stream->bufferEventVector); + + addressPair_Release(&stream->addressPair); + + if (!stream->isClosed) { + stream->isClosed = true; + messenger_Send(forwarder_GetMessenger(stream->forwarder), + missive_Create(MissiveType_ConnectionClosed, stream->id)); + } + + messenger_Send(forwarder_GetMessenger(stream->forwarder), + missive_Create(MissiveType_ConnectionDestroyed, stream->id)); + + if (logger_IsLoggable(stream->logger, LoggerFacility_IO, PARCLogLevel_Info)) { + logger_Log(stream->logger, LoggerFacility_IO, PARCLogLevel_Info, __func__, + "StreamConnection %p destroyed", (void *)stream); + } + + logger_Release(&stream->logger); + parcMemory_Deallocate((void **)&stream); + parcMemory_Deallocate((void **)&ops); + + *opsPtr = NULL; +} + +static bool _streamConnection_IsUp(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _StreamState *stream = + (const _StreamState *)ioOperations_GetClosure(ops); + return stream->isUp; +} + +static bool _streamConnection_IsLocal(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _StreamState *stream = + (const _StreamState *)ioOperations_GetClosure(ops); + return stream->isLocal; +} + +static const Address *_streamConnection_GetRemoteAddress( + const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _StreamState *stream = + (const _StreamState *)ioOperations_GetClosure(ops); + return addressPair_GetRemote(stream->addressPair); +} + +static const AddressPair *_streamConnection_GetAddressPair( + const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _StreamState *stream = + (const _StreamState *)ioOperations_GetClosure(ops); + return stream->addressPair; +} + +static unsigned _streamConnection_GetConnectionId(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _StreamState *stream = + (const _StreamState *)ioOperations_GetClosure(ops); + return stream->id; +} + +bool streamState_SendCommandResponse(IoOperations *ops, + struct iovec *response) { + parcAssertNotNull(ops, "Parameter ops must be non-null"); + parcAssertNotNull(response, "Parameter message must be non-null"); + _StreamState *conn = (_StreamState *)ioOperations_GetClosure(ops); + + bool success = false; + if (conn->isUp) { + PARCEventBuffer *buffer = + parcEventBuffer_GetQueueBufferOutput(conn->bufferEventVector); + size_t buffer_backlog = parcEventBuffer_GetLength(buffer); + parcEventBuffer_Destroy(&buffer); + + if (buffer_backlog < OUTPUT_QUEUE_BYTES) { + if (logger_IsLoggable(conn->logger, LoggerFacility_IO, + PARCLogLevel_Debug)) { + logger_Log( + conn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "connid %u Writing %zu bytes to buffer with backlog %zu bytes", + conn->id, + (response[0].iov_len + + response[1].iov_len), // NEW: take total lenght + buffer_backlog); + } + + // NEW: write directly ino the parcEventQueue without passing through + // message + int failure = + parcEventQueue_Write(conn->bufferEventVector, response[0].iov_base, + response[0].iov_len) + + parcEventQueue_Write(conn->bufferEventVector, response[1].iov_base, + response[1].iov_len); + + if (failure == 0) { + success = true; + } + } else { + if (logger_IsLoggable(conn->logger, LoggerFacility_IO, + PARCLogLevel_Warning)) { + logger_Log(conn->logger, LoggerFacility_IO, PARCLogLevel_Warning, + __func__, + "connid %u Writing to buffer backlog %zu bytes DROP MESSAGE", + conn->id, buffer_backlog); + } + } + } else { + if (logger_IsLoggable(conn->logger, LoggerFacility_IO, + PARCLogLevel_Error)) { + logger_Log( + conn->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "connid %u tried to send to down connection (isUp %d isClosed %d)", + conn->id, conn->isUp, conn->isClosed); + } + } + + return success; +} + +/** + * @function streamConnection_Send + * @abstract Non-destructive send of the message. + * @discussion + * Send uses message_CopyToStreamBuffer, which is a non-destructive write. + * The send may fail if there's no buffer space in the output queue. + * + * @param dummy is ignored. A stream has only one peer. + * @return <#return#> + */ +static bool _streamConnection_Send(IoOperations *ops, const Address *dummy, + Message *message) { + parcAssertNotNull(ops, "Parameter ops must be non-null"); + parcAssertNotNull(message, "Parameter message must be non-null"); + _StreamState *stream = (_StreamState *)ioOperations_GetClosure(ops); + + bool success = false; + if (stream->isUp) { + PARCEventBuffer *buffer = + parcEventBuffer_GetQueueBufferOutput(stream->bufferEventVector); + size_t buffer_backlog = parcEventBuffer_GetLength(buffer); + parcEventBuffer_Destroy(&buffer); + + if (buffer_backlog < OUTPUT_QUEUE_BYTES) { + if (logger_IsLoggable(stream->logger, LoggerFacility_IO, + PARCLogLevel_Debug)) { + logger_Log( + stream->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "connid %u Writing %zu bytes to buffer with backlog %zu bytes", + stream->id, message_Length(message), buffer_backlog); + } + + int failure = message_Write(stream->bufferEventVector, message); + if (failure == 0) { + success = true; + } + } else { + if (logger_IsLoggable(stream->logger, LoggerFacility_IO, + PARCLogLevel_Warning)) { + logger_Log(stream->logger, LoggerFacility_IO, PARCLogLevel_Warning, + __func__, + "connid %u Writing to buffer backlog %zu bytes DROP MESSAGE", + stream->id, buffer_backlog); + } + } + } else { + if (logger_IsLoggable(stream->logger, LoggerFacility_IO, + PARCLogLevel_Error)) { + logger_Log( + stream->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "connid %u tried to send to down connection (isUp %d isClosed %d)", + stream->id, stream->isUp, stream->isClosed); + } + } + + return success; +} + +list_connections_type _streamConnection_GetConnectionType( + const IoOperations *ops) { + return CONN_TCP; +} + +static Ticks _sendProbe(IoOperations *ops, unsigned probeType, + uint8_t *message) { + // we don't need to implemet this here, it is a local connection + return 0; +} + +// ================================================================= +// the actual I/O functions + +int _isACommand(PARCEventBuffer *input) { + size_t bytesAvailable = parcEventBuffer_GetLength(input); + parcAssertTrue(bytesAvailable >= sizeof(header_control_message), + "Called with too short an input: %zu", bytesAvailable); + + uint8_t *msg = parcEventBuffer_Pullup(input, bytesAvailable); + // read first byte of the header + + // first byte: must be a REQUEST_LIGHT + if (msg[0] != 100) { + return LAST_COMMAND_VALUE; + } + + // second byte: must be a command_id + if (msg[1] < 0 || msg[1] >= LAST_COMMAND_VALUE) { + return LAST_COMMAND_VALUE; + } + + return msg[1]; +} + +PARCEventBuffer *_tryReadControlMessage(_StreamState *stream, + PARCEventBuffer *input, + command_id command, + struct iovec **request) { + size_t bytesAvailable = parcEventBuffer_GetLength(input); + + if (stream->nextMessageLength == 0) { + stream->nextMessageLength = + sizeof(header_control_message) + + payloadLengthDaemon[command]; // consider the whole packet. + } + + if (bytesAvailable >= stream->nextMessageLength) { + PARCEventBuffer *message = parcEventBuffer_Create(); + int bytesRead = parcEventBuffer_ReadIntoBuffer(input, message, + stream->nextMessageLength); + parcAssertTrue(bytesRead == stream->nextMessageLength, + "Partial read, expected %zu got %d", + stream->nextMessageLength, bytesRead); + + uint8_t *control = + parcEventBuffer_Pullup(message, stream->nextMessageLength); + if (!(*request = (struct iovec *)parcMemory_AllocateAndClear( + sizeof(struct iovec) * 2))) { + return NULL; + } + (*request)[0].iov_base = control; // header + (*request)[0].iov_len = sizeof(header_control_message); + if (payloadLengthDaemon[command] > 0) { + (*request)[1].iov_base = + control + sizeof(header_control_message); // payload + } else { + (*request)[1].iov_base = NULL; + } + (*request)[1].iov_len = payloadLengthDaemon[command]; + // now reset message length for next packet + + stream->nextMessageLength = 0; + + return message; + } + + return NULL; +} + +static bool _isAnHIcnPacket(PARCEventBuffer *input) { + size_t bytesAvailable = parcEventBuffer_GetLength(input); + parcAssertTrue(bytesAvailable >= sizeof(header_control_message), + "Called with too short an input: %zu", bytesAvailable); + + uint8_t *fh = parcEventBuffer_Pullup(input, sizeof(header_control_message)); + return messageHandler_IsValidHIcnPacket(fh); +} + +static Message *_readMessage(_StreamState *stream, Ticks time, + PARCEventBuffer *input) { + Message *message = message_CreateFromEventBuffer( + input, stream->nextMessageLength, stream->id, time, stream->logger); + + return message; +} + +static void _startNewMessage(_StreamState *stream, PARCEventBuffer *input, + size_t inputBytesAvailable) { + parcAssertTrue(stream->nextMessageLength == 0, + "Invalid state, nextMessageLength not zero: %zu", + stream->nextMessageLength); + parcAssertTrue(inputBytesAvailable >= sizeof(header_control_message), + "read_length not a whole fixed header!: %zd", + inputBytesAvailable); + + // this linearizes the first messageHandler_GetIPv6HeaderLength() bytes of the + // input buffer's iovecs and returns a pointer to it. + uint8_t *fh = parcEventBuffer_Pullup(input, sizeof(header_control_message)); + + // Calculate the total message size based on the fixed header + stream->nextMessageLength = messageHandler_GetTotalPacketLength(fh); +} + +static Message *_tryReadMessage(PARCEventBuffer *input, _StreamState *stream) { + size_t bytesAvailable = parcEventBuffer_GetLength(input); + parcAssertTrue(bytesAvailable >= sizeof(header_control_message), + "Called with too short an input: %zu", bytesAvailable); + + if (stream->nextMessageLength == 0) { + _startNewMessage(stream, input, bytesAvailable); + } + + // This is not an ELSE statement. We can both start a new message then + // check if there's enough bytes to read the whole thing. + + if (bytesAvailable >= stream->nextMessageLength) { + Message *message = + _readMessage(stream, forwarder_GetTicks(stream->forwarder), input); + + // now reset message length for next packet + stream->nextMessageLength = 0; + + return message; + } + + return NULL; +} + +/** + * @function conn_readcb + * @abstract Event callback for reads + * @discussion + * Will read messages off the input. Continues reading as long as we + * can get a header to determine the next message length or as long as we + * can read a complete message. + * + * This function manipulates the read low water mark. (1) read a fixed header + * plus complete message, then set the low water mark to FIXED_HEADER_LEN. (2) + * read a fixed header, but not a complete message, then set low water mark to + * the total mesage length. Using the low water mark like this means the buffer + * event will only trigger on meaningful byte boundaries when we can get actual + * work done. + * + * @param <#param1#> + * @return <#return#> + */ +static void _conn_readcb(PARCEventQueue *event, PARCEventType type, + void *ioOpsVoid) { + command_id command; + IoOperations *ops = (IoOperations *)ioOpsVoid; + _StreamState *stream = (_StreamState *)ioOperations_GetClosure(ops); + + PARCEventBuffer *input = parcEventBuffer_GetQueueBufferInput(event); + + // drain the input buffer + + // notice that we always try to read at least 8 bytes + // (sizeof(header_control_message)). This is enough to read the length of all + // kind of packets + while (parcEventBuffer_GetLength(input) >= sizeof(header_control_message) && + parcEventBuffer_GetLength(input) >= stream->nextMessageLength) { + if ((command = _isACommand(input)) != LAST_COMMAND_VALUE) { + struct iovec *rx; + // Get message from the stream and set the stream->nextMessageLength + PARCEventBuffer *message = + _tryReadControlMessage(stream, input, command, &rx); + // If received correctly the whole message, send to dispatcher + if (message) { + forwarder_ReceiveCommand(stream->forwarder, command, rx, stream->id); + parcEventBuffer_Destroy(&message); + } + + } else if (_isAnHIcnPacket(input)) { + // this is an HIcn packet (here we should distinguish between IPv4 and + // IPv6 tryReadMessage may set nextMessageLength + Message *message = _tryReadMessage(input, stream); + + if (message) { + forwarder_Receive(stream->forwarder, message); + } + + } else { + parcAssertTrue(false, + "(Local stream connection) malformend packet received"); + } + } + + if (stream->nextMessageLength == 0) { + // we don't have the next header, so set it to the header length + streamBuffer_SetWatermark(event, true, false, + sizeof(header_control_message), 0); + } else { + // set it to the packet length + streamBuffer_SetWatermark(event, true, false, stream->nextMessageLength, 0); + } + parcEventBuffer_Destroy(&input); +} + +static void _setConnectionState(_StreamState *stream, bool isUp) { + parcAssertNotNull(stream, "Parameter stream must be non-null"); + + Messenger *messenger = forwarder_GetMessenger(stream->forwarder); + + bool oldStateIsUp = stream->isUp; + stream->isUp = isUp; + + if (oldStateIsUp && !isUp) { + // bring connection DOWN + Missive *missive = missive_Create(MissiveType_ConnectionDown, stream->id); + messenger_Send(messenger, missive); + return; + } + + if (!oldStateIsUp && isUp) { + // bring connection UP + Missive *missive = missive_Create(MissiveType_ConnectionUp, stream->id); + messenger_Send(messenger, missive); + return; + } +} + +static void _conn_eventcb(PARCEventQueue *event, PARCEventQueueEventType events, + void *ioOpsVoid) { + IoOperations *ops = (IoOperations *)ioOpsVoid; + _StreamState *stream = (_StreamState *)ioOperations_GetClosure(ops); + + if (events & PARCEventQueueEventType_Connected) { + if (logger_IsLoggable(stream->logger, LoggerFacility_IO, + PARCLogLevel_Info)) { + logger_Log(stream->logger, LoggerFacility_IO, PARCLogLevel_Info, __func__, + "Connection %u is connected", stream->id); + } + + // if the stream was closed, do not transition to an UP state + if (!stream->isClosed) { + _setConnectionState(stream, true); + } + } else if (events & PARCEventQueueEventType_EOF) { + if (logger_IsLoggable(stream->logger, LoggerFacility_IO, + PARCLogLevel_Info)) { + logger_Log(stream->logger, LoggerFacility_IO, PARCLogLevel_Info, __func__, + "connid %u closed.", stream->id); + } + + parcEventQueue_Disable(stream->bufferEventVector, PARCEventType_Read); + + _setConnectionState(stream, false); + + if (!stream->isClosed) { + stream->isClosed = true; + // this will cause the connection manager to destroy the connection later + messenger_Send(forwarder_GetMessenger(stream->forwarder), + missive_Create(MissiveType_ConnectionClosed, stream->id)); + } + } else if (events & PARCEventQueueEventType_Error) { + if (logger_IsLoggable(stream->logger, LoggerFacility_IO, + PARCLogLevel_Error)) { + logger_Log(stream->logger, LoggerFacility_IO, PARCLogLevel_Error, + __func__, "Got an error on the connection %u: %s", stream->id, + strerror(errno)); + } + + parcEventQueue_Disable(stream->bufferEventVector, + PARCEventType_Read | PARCEventType_Write); + + _setConnectionState(stream, false); + + if (!stream->isClosed) { + stream->isClosed = true; + // this will cause the connection manager to destroy the connection later + messenger_Send(forwarder_GetMessenger(stream->forwarder), + missive_Create(MissiveType_ConnectionClosed, stream->id)); + } + } + /* None of the other events can happen here, since we haven't enabled + * timeouts */ +} diff --git a/hicn-light/src/io/streamConnection.h b/hicn-light/src/io/streamConnection.h new file mode 100755 index 000000000..8eb63a094 --- /dev/null +++ b/hicn-light/src/io/streamConnection.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Methods common to TCP and PF_LOCAL stream-based listeners + */ + +#ifndef streamConnection_h +#define streamConnection_h + +#include +#include +#include +#include +#include + +/** + * @function streamConnection_AcceptConnection + * @abstract Receive a connection from a remote peer + * @discussion + * We are the "server side" of the stream connection, so we need to accept the + * client connection and setup state for her. + * + * @param <#param1#> + * @return <#return#> + */ +IoOperations *streamConnection_AcceptConnection(Forwarder *forwarder, int fd, + AddressPair *pair, + bool isLocal); + +/** + * @function streamConnection_OpenConnection + * @abstract Initiate a connection to a remote peer + * @discussion + * We are the "client side" of the stream connection. We'll create state for + * the peer, but it will be in the "down" state until the connection + * establishes. + * + * For TCP, both address pairs need to be the same address family: both INET + * or both INET6. The remote address must have the complete socket information + * (address, port). The local socket could be wildcarded or may specify down to + * the (address, port) pair. + * + * If the local address is IPADDR_ANY and the port is 0, then it is a normal + * call to "connect" that will use whatever local IP address and whatever local + * port for the connection. If either the address or port is set, the local + * socket will first be bound (via bind(2)), and then call connect(). + * + * AF_UNIX is not yet supported + * + * If there's an error binding to the specified address or connecting to the + * remote address, will return NULL. + * + * @param pair (takes ownership of this) is the complete socket pair of + * (address, port) for each end, if INET or INET6. + * @return NULL on error, otherwise the connections IO operations. + */ +IoOperations *streamConnection_OpenConnection(Forwarder *forwarder, + AddressPair *pair, bool isLocal); + +bool streamState_SendCommandResponse(IoOperations *ops, struct iovec *response); + +#endif // streamConnection_h diff --git a/hicn-light/src/io/tcpListener.c b/hicn-light/src/io/tcpListener.c new file mode 100755 index 000000000..6f0477f5b --- /dev/null +++ b/hicn-light/src/io/tcpListener.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +typedef struct tcp_listener { + Forwarder *forwarder; + Logger *logger; + + PARCEventSocket *listener; + + Address *localAddress; + + unsigned id; + + // is the localAddress as 127.0.0.0 address? + bool isLocalAddressLocal; +} _TcpListener; + +static void _tcpListener_Destroy(_TcpListener **listenerPtr); + +static void _tcpListener_OpsDestroy(ListenerOps **listenerOpsPtr); +static unsigned _tcpListener_OpsGetInterfaceIndex(const ListenerOps *ops); +static const Address *_tcpListener_OpsGetListenAddress(const ListenerOps *ops); +static EncapType _tcpListener_OpsGetEncapType(const ListenerOps *ops); + +static ListenerOps _tcpTemplate = { + .context = NULL, + .destroy = &_tcpListener_OpsDestroy, + .getInterfaceIndex = &_tcpListener_OpsGetInterfaceIndex, + .getListenAddress = &_tcpListener_OpsGetListenAddress, + .getEncapType = &_tcpListener_OpsGetEncapType, + .getSocket = NULL}; + +// STREAM daemon listener callback +static void _tcpListener_Listen(int, struct sockaddr *, int socklen, + void *tcpVoid); + +ListenerOps *tcpListener_CreateInet6(Forwarder *forwarder, + struct sockaddr_in6 sin6) { + _TcpListener *tcp = parcMemory_AllocateAndClear(sizeof(_TcpListener)); + parcAssertNotNull(tcp, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(_TcpListener)); + + tcp->forwarder = forwarder; + tcp->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + + tcp->listener = dispatcher_CreateListener( + forwarder_GetDispatcher(forwarder), _tcpListener_Listen, (void *)tcp, -1, + (struct sockaddr *)&sin6, sizeof(sin6)); + + if (tcp->listener == NULL) { + logger_Log(tcp->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "dispatcher_CreateListener failed to create listener (%d) %s", + errno, strerror(errno)); + logger_Release(&tcp->logger); + parcMemory_Deallocate((void **)&tcp); + return NULL; + } + + tcp->localAddress = addressCreateFromInet6(&sin6); + tcp->id = forwarder_GetNextConnectionId(forwarder); + tcp->isLocalAddressLocal = + parcNetwork_IsSocketLocal((struct sockaddr *)&sin6); + + ListenerOps *ops = parcMemory_AllocateAndClear(sizeof(ListenerOps)); + parcAssertNotNull(ops, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ListenerOps)); + + memcpy(ops, &_tcpTemplate, sizeof(ListenerOps)); + ops->context = tcp; + + if (logger_IsLoggable(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + char *str = addressToString(tcp->localAddress); + logger_Log(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "TcpListener %p created for address %s (isLocal %d)", + (void *)tcp, str, tcp->isLocalAddressLocal); + parcMemory_Deallocate((void **)&str); + } + + return ops; +} + +ListenerOps *tcpListener_CreateInet(Forwarder *forwarder, + struct sockaddr_in sin) { + _TcpListener *tcp = parcMemory_AllocateAndClear(sizeof(_TcpListener)); + parcAssertNotNull(tcp, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(_TcpListener)); + + tcp->forwarder = forwarder; + tcp->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + tcp->listener = dispatcher_CreateListener( + forwarder_GetDispatcher(forwarder), _tcpListener_Listen, (void *)tcp, -1, + (struct sockaddr *)&sin, sizeof(sin)); + + if (tcp->listener == NULL) { + logger_Log(tcp->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "dispatcher_CreateListener failed to create listener (%d) %s", + errno, strerror(errno)); + + logger_Release(&tcp->logger); + parcMemory_Deallocate((void **)&tcp); + return NULL; + } + + tcp->localAddress = addressCreateFromInet(&sin); + tcp->id = forwarder_GetNextConnectionId(forwarder); + tcp->isLocalAddressLocal = parcNetwork_IsSocketLocal((struct sockaddr *)&sin); + + ListenerOps *ops = parcMemory_AllocateAndClear(sizeof(ListenerOps)); + parcAssertNotNull(ops, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ListenerOps)); + + memcpy(ops, &_tcpTemplate, sizeof(ListenerOps)); + ops->context = tcp; + + if (logger_IsLoggable(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + char *str = addressToString(tcp->localAddress); + logger_Log(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "TcpListener %p created for address %s (isLocal %d)", + (void *)tcp, str, tcp->isLocalAddressLocal); + parcMemory_Deallocate((void **)&str); + } + + return ops; +} + +static void _tcpListener_Destroy(_TcpListener **listenerPtr) { + parcAssertNotNull(listenerPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*listenerPtr, + "Parameter must derefernce to non-null pointer"); + _TcpListener *tcp = *listenerPtr; + + if (logger_IsLoggable(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + char *str = addressToString(tcp->localAddress); + logger_Log(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "TcpListener %p destroyed", (void *)tcp); + parcMemory_Deallocate((void **)&str); + } + + logger_Release(&tcp->logger); + dispatcher_DestroyListener(forwarder_GetDispatcher(tcp->forwarder), + &tcp->listener); + addressDestroy(&tcp->localAddress); + parcMemory_Deallocate((void **)&tcp); + *listenerPtr = NULL; +} + +// ================================================== + +static void _tcpListener_Listen(int fd, struct sockaddr *sa, int socklen, + void *tcpVoid) { + _TcpListener *tcp = (_TcpListener *)tcpVoid; + + Address *remote; + + switch (sa->sa_family) { + case AF_INET: + remote = addressCreateFromInet((struct sockaddr_in *)sa); + break; + + case AF_INET6: + remote = addressCreateFromInet6((struct sockaddr_in6 *)sa); + break; + + default: + parcTrapIllegalValue(sa, "Expected INET or INET6, got %d", sa->sa_family); + abort(); + } + + AddressPair *pair = addressPair_Create(tcp->localAddress, remote); + + IoOperations *ops = streamConnection_AcceptConnection( + tcp->forwarder, fd, pair, tcp->isLocalAddressLocal); + Connection *conn = connection_Create(ops); + + connectionTable_Add(forwarder_GetConnectionTable(tcp->forwarder), conn); + + if (logger_IsLoggable(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + logger_Log(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "TcpListener %p listen started", (void *)tcp); + } + + addressDestroy(&remote); +} + +static void _tcpListener_OpsDestroy(ListenerOps **listenerOpsPtr) { + ListenerOps *ops = *listenerOpsPtr; + _TcpListener *tcp = (_TcpListener *)ops->context; + _tcpListener_Destroy(&tcp); + parcMemory_Deallocate((void **)&ops); + *listenerOpsPtr = NULL; +} + +static unsigned _tcpListener_OpsGetInterfaceIndex(const ListenerOps *ops) { + _TcpListener *tcp = (_TcpListener *)ops->context; + return tcp->id; +} + +static const Address *_tcpListener_OpsGetListenAddress(const ListenerOps *ops) { + _TcpListener *tcp = (_TcpListener *)ops->context; + return tcp->localAddress; +} + +static EncapType _tcpListener_OpsGetEncapType(const ListenerOps *ops) { + return ENCAP_TCP; +} diff --git a/hicn-light/src/io/tcpListener.h b/hicn-light/src/io/tcpListener.h new file mode 100755 index 000000000..c5d1e33af --- /dev/null +++ b/hicn-light/src/io/tcpListener.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file tcpListener.h + * @brief Listens for in coming TCP connections + * + * This is the "server socket" of hicn-light for TCP connections. The actual + * I/O is handled by {@link StreamConnection}. + * + */ + +#ifndef tcpListener_h +#define tcpListener_h + +#include +#include +#include +#include + +ListenerOps *tcpListener_CreateInet6(Forwarder *forwarder, + struct sockaddr_in6 sin6); +ListenerOps *tcpListener_CreateInet(Forwarder *forwarder, + struct sockaddr_in sin); +#endif // tcpListener_h diff --git a/hicn-light/src/io/tcpTunnel.c b/hicn-light/src/io/tcpTunnel.c new file mode 100755 index 000000000..a2bf2bd30 --- /dev/null +++ b/hicn-light/src/io/tcpTunnel.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +IoOperations *tcpTunnel_Create(Forwarder *forwarder, + const Address *localAddress, + const Address *remoteAddress) { + IoOperations *ops = NULL; + + address_type localType = addressGetType(localAddress); + address_type remoteType = addressGetType(remoteAddress); + + if (localType == remoteType) { + AddressPair *pair = addressPair_Create(localAddress, remoteAddress); + bool isLocal = false; + + ops = streamConnection_OpenConnection(forwarder, pair, isLocal); + } + + return ops; +} diff --git a/hicn-light/src/io/tcpTunnel.h b/hicn-light/src/io/tcpTunnel.h new file mode 100755 index 000000000..4daa7d032 --- /dev/null +++ b/hicn-light/src/io/tcpTunnel.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file tcpTunnel.h + * @brief Establish a tunnel to a remote system + * + */ + +#ifndef tcpTunnel_h +#define tcpTunnel_h + +#include +#include +#include +#include + +/** + */ +// IoOperations *tcpTunnel_CreateOnListener(Forwarder *forwarder, +// ListenerOps *localListener, +// const Address *remoteAddress); + +/** + */ +IoOperations *tcpTunnel_Create(Forwarder *forwarder, + const Address *localAddress, + const Address *remoteAddress); + +#endif // tcpTunnel_h diff --git a/hicn-light/src/io/udpConnection.c b/hicn-light/src/io/udpConnection.c new file mode 100755 index 000000000..2aa6edc51 --- /dev/null +++ b/hicn-light/src/io/udpConnection.c @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Embodies the reader/writer for a UDP connection + * + * NB The Send() function may overflow the output buffer + * + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +typedef struct udp_state { + Forwarder *forwarder; + Logger *logger; + + // the udp listener socket we receive packets on + int udpListenerSocket; + + AddressPair *addressPair; + + // We need to access this all the time, so grab it out + // of the addressPair; + struct sockaddr *peerAddress; + socklen_t peerAddressLength; + + bool isLocal; + bool isUp; + unsigned id; + + unsigned delay; +} _UdpState; + +// Prototypes +static bool _send(IoOperations *ops, const Address *nexthop, Message *message); +static const Address *_getRemoteAddress(const IoOperations *ops); +static const AddressPair *_getAddressPair(const IoOperations *ops); +static unsigned _getConnectionId(const IoOperations *ops); +static bool _isUp(const IoOperations *ops); +static bool _isLocal(const IoOperations *ops); +static void _destroy(IoOperations **opsPtr); +static list_connections_type _getConnectionType(const IoOperations *ops); +static Ticks _sendProbe(IoOperations *ops, unsigned probeType, + uint8_t *message); +/* + * This assigns a unique pointer to the void * which we use + * as a GUID for this class. + */ +static const void *_IoOperationsGuid = __FILE__; + +/* + * Return our GUID + */ +static const void *_streamConnection_Class(const IoOperations *ops) { + return _IoOperationsGuid; +} + +static IoOperations _template = {.closure = NULL, + .send = &_send, + .getRemoteAddress = &_getRemoteAddress, + .getAddressPair = &_getAddressPair, + .getConnectionId = &_getConnectionId, + .isUp = &_isUp, + .isLocal = &_isLocal, + .destroy = &_destroy, + .class = &_streamConnection_Class, + .getConnectionType = &_getConnectionType, + .sendProbe = &_sendProbe}; + +// ================================================================= + +static void _setConnectionState(_UdpState *Udp, bool isUp); +static bool _saveSockaddr(_UdpState *udpConnState, const AddressPair *pair); + +IoOperations *udpConnection_Create(Forwarder *forwarder, int fd, + const AddressPair *pair, bool isLocal) { + IoOperations *io_ops = NULL; + + _UdpState *udpConnState = parcMemory_AllocateAndClear(sizeof(_UdpState)); + parcAssertNotNull(udpConnState, + "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(_UdpState)); + + udpConnState->forwarder = forwarder; + udpConnState->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + + bool saved = _saveSockaddr(udpConnState, pair); + if (saved) { + udpConnState->udpListenerSocket = fd; + udpConnState->id = forwarder_GetNextConnectionId(forwarder); + udpConnState->addressPair = addressPair_Acquire(pair); + udpConnState->isLocal = isLocal; + + // allocate a connection + io_ops = parcMemory_AllocateAndClear(sizeof(IoOperations)); + parcAssertNotNull(io_ops, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(IoOperations)); + memcpy(io_ops, &_template, sizeof(IoOperations)); + io_ops->closure = udpConnState; + + _setConnectionState(udpConnState, true); + + if (logger_IsLoggable(udpConnState->logger, LoggerFacility_IO, + PARCLogLevel_Info)) { + char *str = addressPair_ToString(udpConnState->addressPair); + logger_Log(udpConnState->logger, LoggerFacility_IO, PARCLogLevel_Info, + __func__, + "UdpConnection %p created for address %s (isLocal %d)", + (void *)udpConnState, str, udpConnState->isLocal); + free(str); + } + + messenger_Send( + forwarder_GetMessenger(forwarder), + missive_Create(MissiveType_ConnectionCreate, udpConnState->id)); + messenger_Send(forwarder_GetMessenger(forwarder), + missive_Create(MissiveType_ConnectionUp, udpConnState->id)); + } else { + // _saveSockaddr will already log an error, no need for extra log message + // here + logger_Release(&udpConnState->logger); + parcMemory_Deallocate((void **)&udpConnState); + } + + return io_ops; +} + +// ================================================================= +// I/O Operations implementation + +static void _destroy(IoOperations **opsPtr) { + parcAssertNotNull(opsPtr, "Parameter opsPtr must be non-null double pointer"); + parcAssertNotNull(*opsPtr, + "Parameter opsPtr must dereference to non-null pointer"); + + IoOperations *ops = *opsPtr; + parcAssertNotNull(ioOperations_GetClosure(ops), + "ops->context must not be null"); + + _UdpState *udpConnState = (_UdpState *)ioOperations_GetClosure(ops); + addressPair_Release(&udpConnState->addressPair); + parcMemory_Deallocate((void **)&(udpConnState->peerAddress)); + + messenger_Send( + forwarder_GetMessenger(udpConnState->forwarder), + missive_Create(MissiveType_ConnectionDestroyed, udpConnState->id)); + + if (logger_IsLoggable(udpConnState->logger, LoggerFacility_IO, + PARCLogLevel_Info)) { + logger_Log(udpConnState->logger, LoggerFacility_IO, PARCLogLevel_Info, + __func__, "UdpConnection %p destroyed", (void *)udpConnState); + } + + // do not close udp->udpListenerSocket, the listener will close + // that when its done + + logger_Release(&udpConnState->logger); + parcMemory_Deallocate((void **)&udpConnState); + parcMemory_Deallocate((void **)&ops); + + *opsPtr = NULL; +} + +static bool _isUp(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _UdpState *udpConnState = + (const _UdpState *)ioOperations_GetClosure(ops); + return udpConnState->isUp; +} + +static bool _isLocal(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _UdpState *udpConnState = + (const _UdpState *)ioOperations_GetClosure(ops); + return udpConnState->isLocal; +} + +static const Address *_getRemoteAddress(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _UdpState *udpConnState = + (const _UdpState *)ioOperations_GetClosure(ops); + return addressPair_GetRemote(udpConnState->addressPair); +} + +static const AddressPair *_getAddressPair(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _UdpState *udpConnState = + (const _UdpState *)ioOperations_GetClosure(ops); + return udpConnState->addressPair; +} + +static unsigned _getConnectionId(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _UdpState *udpConnState = + (const _UdpState *)ioOperations_GetClosure(ops); + return udpConnState->id; +} + +/** + * @function metisUdpConnection_Send + * @abstract Non-destructive send of the message. + * @discussion + * sends a message to the peer. + * + * @param dummy is ignored. A udp connection has only one peer. + * @return <#return#> + */ +static bool _send(IoOperations *ops, const Address *dummy, Message *message) { + parcAssertNotNull(ops, "Parameter ops must be non-null"); + parcAssertNotNull(message, "Parameter message must be non-null"); + _UdpState *udpConnState = (_UdpState *)ioOperations_GetClosure(ops); + + // NAT for HICN + // in this particular connection we don't need natting beacause we send the + // packet to the next hop using upd connection + +#if 0 + if((hicnConnState->peerAddressLength == sizeof(struct sockaddr_in)) || (hicnConnState->localAddressLength == sizeof(struct sockaddr_in))) + return false; + + if(message_GetType(message) = MessagePacketType_ContentObject){ + //this is a data packet. We need to put the remote address in the destination field + messageHandler_SetDestination_IPv6((uint8_t *) message_FixedHeader(message), + &((struct sockaddr_in6 *) hicnConnState->peerAddress)->sin6_addr); + + } else if (message_GetType(message) == MessagePacketType_Interest) { + //this si an interest packet. We need to put the local address in the source field + messageHandler_SetSource_IPv6((uint8_t *) message_FixedHeader(message), + &((struct sockaddr_in6 *) hicnConnState->localAddress)->sin6_addr); + + //only in this case we may need to set the probeDestAddress + if(hicnConnState->refreshProbeDestAddress){ + _refreshProbeDestAddress(hicnConnState, message_FixedHeader(message)); + } + + } else if (message_GetType(message) == MessagePacketType_WldrNotification) { + //here we don't need to do anything for now + }else{ + //unkown packet + if (logger_IsLoggable(hicnConnState->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + logger_Log(hicnConnState->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "connid %u can't parse the message", + hicnConnState->id); + } + return false; + } +#endif + + ssize_t writeLength = + sendto(udpConnState->udpListenerSocket, message_FixedHeader(message), + message_Length(message), 0, udpConnState->peerAddress, + udpConnState->peerAddressLength); + + if (writeLength < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return false; + } else { + // this print is for debugging + printf("Incorrect write length %zd, expected %zd: (%d) %s\n", writeLength, + message_Length(message), errno, strerror(errno)); + return false; + } + } + + return true; +} + +static list_connections_type _getConnectionType(const IoOperations *ops) { + return CONN_UDP; +} + +static Ticks _sendProbe(IoOperations *ops, unsigned probeType, + uint8_t *message) { +#if 0 + parcAssertNotNull(ops, "Parameter ops must be non-null"); + _MetisUdpState *udpConnState = (_MetisUdpState *) metisIoOperations_GetClosure(ops); + + + uint8_t *pkt; + size_t pkt_size = 8; + pkt = (uint8_t *) malloc(sizeof(uint8_t) * pkt_size); + for (unsigned i = 0; i < pkt_size; i++) { + pkt[i] = 0; + } + pkt[0] = 1; //type + pkt[1] = probeType; //packet type + pkt[6] = 8; //header len (16bit, network order) + + ssize_t writeLen = sendto(udpConnState->udpListenerSocket, pkt, pkt_size, 0, udpConnState->peerAddress, udpConnState->peerAddressLength); + + if (writeLen < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + free(pkt); + return 0; + } else { + //this print is for debugging + printf("Incorrect write length %zd, expected %zd: (%d) %s\n", writeLen, pkt_size, errno, strerror(errno)); + free(pkt); + return 0; + } + } + + free(pkt); + return metisForwarder_GetTicks(udpConnState->metis); +#endif + return 0; +} + +// ================================================================= +// Internal API + +static bool _saveSockaddr(_UdpState *udpConnState, const AddressPair *pair) { + bool success = false; + const Address *remoteAddress = addressPair_GetRemote(pair); + + switch (addressGetType(remoteAddress)) { + case ADDR_INET: { + size_t bytes = sizeof(struct sockaddr_in); + udpConnState->peerAddress = parcMemory_Allocate(bytes); + parcAssertNotNull(udpConnState->peerAddress, + "parcMemory_Allocate(%zu) returned NULL", bytes); + + addressGetInet(remoteAddress, + (struct sockaddr_in *)udpConnState->peerAddress); + udpConnState->peerAddressLength = (socklen_t)bytes; + + success = true; + break; + } + + case ADDR_INET6: { + size_t bytes = sizeof(struct sockaddr_in6); + udpConnState->peerAddress = parcMemory_Allocate(bytes); + parcAssertNotNull(udpConnState->peerAddress, + "parcMemory_Allocate(%zu) returned NULL", bytes); + + addressGetInet6(remoteAddress, + (struct sockaddr_in6 *)udpConnState->peerAddress); + udpConnState->peerAddressLength = (socklen_t)bytes; + + success = true; + break; + } + + default: + if (logger_IsLoggable(udpConnState->logger, LoggerFacility_IO, + PARCLogLevel_Error)) { + char *str = addressToString(remoteAddress); + logger_Log(udpConnState->logger, LoggerFacility_IO, PARCLogLevel_Error, + __func__, "Remote address is not INET or INET6: %s", str); + parcMemory_Deallocate((void **)&str); + } + break; + } + return success; +} + +static void _setConnectionState(_UdpState *udpConnState, bool isUp) { + parcAssertNotNull(udpConnState, "Parameter Udp must be non-null"); + + Messenger *messenger = forwarder_GetMessenger(udpConnState->forwarder); + + bool oldStateIsUp = udpConnState->isUp; + udpConnState->isUp = isUp; + + if (oldStateIsUp && !isUp) { + // bring connection DOWN + Missive *missive = + missive_Create(MissiveType_ConnectionDown, udpConnState->id); + messenger_Send(messenger, missive); + return; + } + + if (!oldStateIsUp && isUp) { + // bring connection UP + Missive *missive = + missive_Create(MissiveType_ConnectionUp, udpConnState->id); + messenger_Send(messenger, missive); + return; + } +} diff --git a/hicn-light/src/io/udpConnection.h b/hicn-light/src/io/udpConnection.h new file mode 100755 index 000000000..122f332d5 --- /dev/null +++ b/hicn-light/src/io/udpConnection.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file udpConnection.h + * @brief Represents a UDP connection (socket) for the connection table + * + * <#Detailed Description#> + * + */ + +#ifndef udpConnection_h +#define udpConnection_h + +#include +#include +#include +#include + +/** + * Creates a UDP connection that can send to the remote address + * + * The address pair must both be same type (i.e. INET or INET6). + * + * @param [in] metis An allocated MetisForwarder (saves reference) + * @param [in] fd The socket to use + * @param [in] pair An allocated address pair for the connection (saves + * reference) + * @param [in] isLocal determines if the remote address is on the current system + * + * @retval non-null An allocated Io operations + * @retval null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +IoOperations *udpConnection_Create(Forwarder *forwarder, int fd, + const AddressPair *pair, bool isLocal); +#endif // udpConnection_h diff --git a/hicn-light/src/io/udpListener.c b/hicn-light/src/io/udpListener.c new file mode 100755 index 000000000..31c0e673b --- /dev/null +++ b/hicn-light/src/io/udpListener.c @@ -0,0 +1,533 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#ifdef WITH_MAPME +#include +#endif /* WITH_MAPME */ + +#define IPv4 4 +#define IPv6 6 + +struct udp_listener { + Forwarder *forwarder; + Logger *logger; + + PARCEvent *udp_event; + SocketType udp_socket; + uint16_t port; + + unsigned id; + Address *localAddress; +}; + +static void _destroy(ListenerOps **listenerOpsPtr); +static unsigned _getInterfaceIndex(const ListenerOps *ops); +static const Address *_getListenAddress(const ListenerOps *ops); +static EncapType _getEncapType(const ListenerOps *ops); +static int _getSocket(const ListenerOps *ops); + +static ListenerOps udpTemplate = {.context = NULL, + .destroy = &_destroy, + .getInterfaceIndex = &_getInterfaceIndex, + .getListenAddress = &_getListenAddress, + .getEncapType = &_getEncapType, + .getSocket = &_getSocket}; + +static void _readcb(int fd, PARCEventType what, void *udpVoid); + +ListenerOps *udpListener_CreateInet6(Forwarder *forwarder, + struct sockaddr_in6 sin6) { + ListenerOps *ops = NULL; + + UdpListener *udp = parcMemory_AllocateAndClear(sizeof(UdpListener)); + parcAssertNotNull(udp, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(UdpListener)); + udp->forwarder = forwarder; + udp->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + udp->localAddress = addressCreateFromInet6(&sin6); + udp->id = forwarder_GetNextConnectionId(forwarder); + + udp->udp_socket = socket(AF_INET6, SOCK_DGRAM, 0); + parcAssertFalse(udp->udp_socket < 0, "Error opening UDP socket: (%d) %s", + errno, strerror(errno)); + + // Set non-blocking flag + int flags = fcntl(udp->udp_socket, F_GETFL, NULL); + parcAssertTrue(flags != -1, + "fcntl failed to obtain file descriptor flags (%d)", errno); + int failure = fcntl(udp->udp_socket, F_SETFL, flags | O_NONBLOCK); + parcAssertFalse(failure, "fcntl failed to set file descriptor flags (%d)", + errno); + + int one = 1; + // don't hang onto address after listener has closed + failure = setsockopt(udp->udp_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&one, + (socklen_t)sizeof(one)); + parcAssertFalse(failure, "failed to set REUSEADDR on socket(%d)", errno); + + failure = bind(udp->udp_socket, (struct sockaddr *)&sin6, sizeof(sin6)); + if (failure == 0) { + udp->udp_event = + dispatcher_CreateNetworkEvent(forwarder_GetDispatcher(forwarder), true, + _readcb, (void *)udp, udp->udp_socket); + dispatcher_StartNetworkEvent(forwarder_GetDispatcher(forwarder), + udp->udp_event); + + ops = parcMemory_AllocateAndClear(sizeof(ListenerOps)); + parcAssertNotNull(ops, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ListenerOps)); + memcpy(ops, &udpTemplate, sizeof(ListenerOps)); + ops->context = udp; + + if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + char *str = addressToString(udp->localAddress); + logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "UdpListener %p created for address %s", (void *)udp, str); + parcMemory_Deallocate((void **)&str); + } + } else { + if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Error)) { + int myerrno = errno; + char *str = addressToString(udp->localAddress); + logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "Error binding UDP socket to address %s: (%d) %s", str, + myerrno, strerror(myerrno)); + parcMemory_Deallocate((void **)&str); + } + + close(udp->udp_socket); + addressDestroy(&udp->localAddress); + logger_Release(&udp->logger); + parcMemory_Deallocate((void **)&udp); + } + + return ops; +} + +ListenerOps *udpListener_CreateInet(Forwarder *forwarder, + struct sockaddr_in sin) { + ListenerOps *ops = NULL; + + UdpListener *udp = parcMemory_AllocateAndClear(sizeof(UdpListener)); + parcAssertNotNull(udp, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(UdpListener)); + udp->forwarder = forwarder; + udp->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + udp->localAddress = addressCreateFromInet(&sin); + udp->id = forwarder_GetNextConnectionId(forwarder); + + udp->udp_socket = socket(AF_INET, SOCK_DGRAM, 0); + parcAssertFalse(udp->udp_socket < 0, "Error opening UDP socket: (%d) %s", + errno, strerror(errno)); + + // Set non-blocking flag + int flags = fcntl(udp->udp_socket, F_GETFL, NULL); + parcAssertTrue(flags != -1, + "fcntl failed to obtain file descriptor flags (%d)", errno); + int failure = fcntl(udp->udp_socket, F_SETFL, flags | O_NONBLOCK); + parcAssertFalse(failure, "fcntl failed to set file descriptor flags (%d)", + errno); + + int one = 1; + // don't hang onto address after listener has closed + failure = setsockopt(udp->udp_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&one, + (socklen_t)sizeof(one)); + parcAssertFalse(failure, "failed to set REUSEADDR on socket(%d)", errno); + + failure = bind(udp->udp_socket, (struct sockaddr *)&sin, sizeof(sin)); + if (failure == 0) { + udp->udp_event = + dispatcher_CreateNetworkEvent(forwarder_GetDispatcher(forwarder), true, + _readcb, (void *)udp, udp->udp_socket); + dispatcher_StartNetworkEvent(forwarder_GetDispatcher(forwarder), + udp->udp_event); + + ops = parcMemory_AllocateAndClear(sizeof(ListenerOps)); + parcAssertNotNull(ops, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ListenerOps)); + memcpy(ops, &udpTemplate, sizeof(ListenerOps)); + ops->context = udp; + + if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + char *str = addressToString(udp->localAddress); + logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "UdpListener %p created for address %s", (void *)udp, str); + parcMemory_Deallocate((void **)&str); + } + } else { + if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Error)) { + int myerrno = errno; + char *str = addressToString(udp->localAddress); + logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "Error binding UDP socket to address %s: (%d) %s", str, + myerrno, strerror(myerrno)); + parcMemory_Deallocate((void **)&str); + } + + close(udp->udp_socket); + addressDestroy(&udp->localAddress); + logger_Release(&udp->logger); + parcMemory_Deallocate((void **)&udp); + } + + return ops; +} + +static void udpListener_Destroy(UdpListener **listenerPtr) { + parcAssertNotNull(listenerPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*listenerPtr, + "Parameter must derefernce to non-null pointer"); + + UdpListener *udp = *listenerPtr; + + if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "UdpListener %p destroyed", (void *)udp); + } + + close(udp->udp_socket); + addressDestroy(&udp->localAddress); + dispatcher_DestroyNetworkEvent(forwarder_GetDispatcher(udp->forwarder), + &udp->udp_event); + logger_Release(&udp->logger); + parcMemory_Deallocate((void **)&udp); + *listenerPtr = NULL; +} + +static void _destroy(ListenerOps **listenerOpsPtr) { + ListenerOps *ops = *listenerOpsPtr; + UdpListener *udp = (UdpListener *)ops->context; + udpListener_Destroy(&udp); + parcMemory_Deallocate((void **)&ops); + *listenerOpsPtr = NULL; +} + +static unsigned _getInterfaceIndex(const ListenerOps *ops) { + UdpListener *udp = (UdpListener *)ops->context; + return udp->id; +} + +static const Address *_getListenAddress(const ListenerOps *ops) { + UdpListener *udp = (UdpListener *)ops->context; + return udp->localAddress; +} + +static EncapType _getEncapType(const ListenerOps *ops) { return ENCAP_UDP; } + +static int _getSocket(const ListenerOps *ops) { + UdpListener *udp = (UdpListener *)ops->context; + return (int)udp->udp_socket; +} + +// void +// udpListener_SetPacketType(ListenerOps *ops, MessagePacketType type) +//{ +// return; +//} + +// ===================================================================== + +/** + * @function peekMesageLength + * @abstract Peek at the next packet to learn its length by reading the fixed + * header + * @discussion + * <#Discussion#> + * + * @param <#param1#> + * @return <#return#> + */ +static size_t _peekMessageLength(UdpListener *udp, int fd, + struct sockaddr *peerIpAddress, + socklen_t *peerIpAddressLengthPtr) { + // to be fast I try to use just ipv6, this needs to be validated for ipv4 + + size_t packetLength = 0; + + uint8_t fixedHeader[messageHandler_GetIPHeaderLength(IPv6)]; + + // peek at the UDP packet and read in the fixed header. + // Also returns the socket information for the remote peer + + ssize_t res = recvfrom( + fd, fixedHeader, messageHandler_GetIPHeaderLength(IPv6), MSG_PEEK, + (struct sockaddr *)peerIpAddress, peerIpAddressLengthPtr); + + if (res == messageHandler_GetIPHeaderLength(IPv6)) { + packetLength = + messageHandler_GetTotalPacketLength((const uint8_t *)&fixedHeader); + } else { + if (res < 0) { + printf("error while readin packet\n"); + } + } + + return packetLength; +} + +/** + * @function _constructAddressPair + * @abstract Creates the address pair that uniquely identifies the connection + * @discussion + * The peerIpAddress must be of AF_INET or AF_INET6 family. + * + * @param <#param1#> + * @return Allocated MetisAddressPair, must be destroyed + */ +static AddressPair *_constructAddressPair(UdpListener *udp, + struct sockaddr *peerIpAddress, + socklen_t peerIpAddressLength) { + Address *remoteAddress; + + switch (peerIpAddress->sa_family) { + case AF_INET: + remoteAddress = + addressCreateFromInet((struct sockaddr_in *)peerIpAddress); + break; + + case AF_INET6: + remoteAddress = + addressCreateFromInet6((struct sockaddr_in6 *)peerIpAddress); + break; + + default: + parcTrapIllegalValue(peerIpAddress, + "Peer address unrecognized family for IP: %d", + peerIpAddress->sa_family); + } + + AddressPair *pair = addressPair_Create(udp->localAddress, remoteAddress); + addressDestroy(&remoteAddress); + + return pair; +} + +/** + * @function _lookupConnectionId + * @abstract Lookup a connection in the connection table + * @discussion + * Looks up the connection in the connection table and returns the connection + * id if it exists. + * + * @param outputConnectionIdPtr is the output parameter + * @return true if connection found and outputConnectionIdPtr set + */ +static bool _lookupConnectionId(UdpListener *udp, AddressPair *pair, + unsigned *outputConnectionIdPtr) { + ConnectionTable *connTable = forwarder_GetConnectionTable(udp->forwarder); + + const Connection *conn = connectionTable_FindByAddressPair(connTable, pair); + if (conn) { + *outputConnectionIdPtr = connection_GetConnectionId(conn); + return true; + } else { + *outputConnectionIdPtr = 0; + return false; + } +} + +/** + * @function _createNewConnection + * @abstract Creates a new Metis connection for the peer + * @discussion + * PRECONDITION: you know there's not an existing connection with the address + * pair + * + * Creates a new connection and adds it to the connection table. + * + * @param <#param1#> + * @return The connection id for the new connection + */ + +static unsigned _createNewConnection(UdpListener *udp, int fd, + const AddressPair *pair) { + bool isLocal = false; + + // metisUdpConnection_Create takes ownership of the pair + IoOperations *ops = udpConnection_Create(udp->forwarder, fd, pair, isLocal); + Connection *conn = connection_Create(ops); + // connection_AllowWldrAutoStart(conn); + + connectionTable_Add(forwarder_GetConnectionTable(udp->forwarder), conn); + unsigned connid = ioOperations_GetConnectionId(ops); + + return connid; +} + +static void _handleProbeMessage(UdpListener *udp, uint8_t *msgBuffer) { + // TODO + parcMemory_Deallocate((void **)&msgBuffer); +} + +static void _handleWldrNotification(UdpListener *udp, unsigned connId, + uint8_t *msgBuffer) { + const Connection *conn = connectionTable_FindById( + forwarder_GetConnectionTable(udp->forwarder), connId); + if (conn == NULL) { + return; + } + + Message *message = message_CreateFromByteArray( + connId, msgBuffer, MessagePacketType_WldrNotification, + forwarder_GetTicks(udp->forwarder), forwarder_GetLogger(udp->forwarder)); + + connection_HandleWldrNotification((Connection *)conn, message); + + message_Release(&message); +} + +static Message *_readMessage(UdpListener *udp, int fd, size_t packetLength, + AddressPair *pair) { + uint8_t *msgBuffer = parcMemory_AllocateAndClear(packetLength); + + ssize_t readLength = read(fd, msgBuffer, packetLength); + + Message *message = NULL; + + if (readLength < 0) { + printf("read failed %d: (%d) %s\n", fd, errno, strerror(errno)); + return message; + } + + unsigned connid = 0; + bool foundConnection = _lookupConnectionId(udp, pair, &connid); + + if (readLength == packetLength) { + // we need to check if it is a valid packet + if (messageHandler_IsTCP(msgBuffer)) { + MessagePacketType pktType; + + if (messageHandler_IsData(msgBuffer)) { + pktType = MessagePacketType_ContentObject; + if (!foundConnection) { + parcMemory_Deallocate((void **)&msgBuffer); + return message; + } + } else if (messageHandler_IsInterest(msgBuffer)) { + pktType = MessagePacketType_Interest; + if (!foundConnection) { + connid = _createNewConnection(udp, fd, pair); + } + } else { + printf("Got a packet that is not a data nor an interest, drop it!\n"); + parcMemory_Deallocate((void **)&msgBuffer); + return message; + } + + message = message_CreateFromByteArray( + connid, msgBuffer, pktType, forwarder_GetTicks(udp->forwarder), + forwarder_GetLogger(udp->forwarder)); + + if (message == NULL) { + parcMemory_Deallocate((void **)&msgBuffer); + } + } else if (messageHandler_IsWldrNotification(msgBuffer)) { + _handleWldrNotification(udp, connid, msgBuffer); + } else if (messageHandler_IsLoadBalancerProbe(msgBuffer)) { + _handleProbeMessage(udp, msgBuffer); + } +#ifdef WITH_MAPME + else if (mapMe_isMapMe(msgBuffer)) { + forwarder_ProcessMapMe(udp->forwarder, msgBuffer, connid); + } +#endif /* WITH_MAPME */ + } + + return message; +} + +static void _receivePacket(UdpListener *udp, int fd, size_t packetLength, + struct sockaddr_storage *peerIpAddress, + socklen_t peerIpAddressLength) { + AddressPair *pair = _constructAddressPair( + udp, (struct sockaddr *)peerIpAddress, peerIpAddressLength); + + Message *message = _readMessage(udp, fd, packetLength, pair); + addressPair_Release(&pair); + + if (message) { + forwarder_Receive(udp->forwarder, message); + } else { + return; + } +} + +static void _readFrameToDiscard(UdpListener *udp, int fd) { + // we need to discard the frame. Read 1 byte. This will clear it off the + // stack. + uint8_t buffer; + ssize_t nread = read(fd, &buffer, 1); + + if (nread == 1) { + if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "Discarded frame from fd %d", fd); + } + } else if (nread < 0) { + if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Error)) { + logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "Error trying to discard frame from fd %d: (%d) %s", fd, errno, + strerror(errno)); + } + } +} + +static void _readcb(int fd, PARCEventType what, void *udpVoid) { + UdpListener *udp = (UdpListener *)udpVoid; + + if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "%s socket %d what %s%s%s%s data %p", __func__, fd, + (what & PARCEventType_Timeout) ? " timeout" : "", + (what & PARCEventType_Read) ? " read" : "", + (what & PARCEventType_Write) ? " write" : "", + (what & PARCEventType_Signal) ? " signal" : "", udpVoid); + } + + if (what & PARCEventType_Read) { + struct sockaddr_storage peerIpAddress; + socklen_t peerIpAddressLength = sizeof(peerIpAddress); + + size_t packetLength = _peekMessageLength( + udp, fd, (struct sockaddr *)&peerIpAddress, &peerIpAddressLength); + + if (packetLength > 0) { + _receivePacket(udp, fd, packetLength, &peerIpAddress, + peerIpAddressLength); + } else { + _readFrameToDiscard(udp, fd); + } + } +} diff --git a/hicn-light/src/io/udpListener.h b/hicn-light/src/io/udpListener.h new file mode 100755 index 000000000..1cf3bd887 --- /dev/null +++ b/hicn-light/src/io/udpListener.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef udpListener_h +#define udpListener_h + +#include +#include +#include +#include + +struct udp_listener; +typedef struct udp_listener UdpListener; + +ListenerOps *udpListener_CreateInet6(Forwarder *forwarder, + struct sockaddr_in6 sin6); +ListenerOps *udpListener_CreateInet(Forwarder *forwarder, + struct sockaddr_in sin); +// void udpListener_SetPacketType(ListenerOps *ops, MessagePacketType type); +#endif // udpListener_h diff --git a/hicn-light/src/io/udpTunnel.c b/hicn-light/src/io/udpTunnel.c new file mode 100755 index 000000000..d06a35ce6 --- /dev/null +++ b/hicn-light/src/io/udpTunnel.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +IoOperations *udpTunnel_CreateOnListener(Forwarder *forwarder, + ListenerOps *localListener, + const Address *remoteAddress) { + parcAssertNotNull(forwarder, "Parameter metis must be non-null"); + parcAssertNotNull(localListener, "Parameter localListener must be non-null"); + parcAssertNotNull(remoteAddress, "Parameter remoteAddress must be non-null"); + + Logger *logger = forwarder_GetLogger(forwarder); + + IoOperations *ops = NULL; + if (localListener->getEncapType(localListener) == ENCAP_UDP) { + const Address *localAddress = + localListener->getListenAddress(localListener); + address_type localType = addressGetType(localAddress); + address_type remoteType = addressGetType(remoteAddress); + + if (localType == remoteType) { + AddressPair *pair = addressPair_Create(localAddress, remoteAddress); + bool isLocal = false; + int fd = localListener->getSocket(localListener); + // udpListener_SetPacketType(localListener, + // MessagePacketType_ContentObject); + ops = udpConnection_Create(forwarder, fd, pair, isLocal); + + addressPair_Release(&pair); + } else { + if (logger_IsLoggable(logger, LoggerFacility_IO, PARCLogLevel_Error)) { + logger_Log(logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "Local listener of type %s and remote type %s, cannot " + "establish tunnel", + addressTypeToString(localType), + addressTypeToString(remoteType)); + } + } + } else { + if (logger_IsLoggable(logger, LoggerFacility_IO, PARCLogLevel_Error)) { + logger_Log(logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "Local listener %p is not type UDP, cannot establish tunnel", + (void *)localListener); + } + } + + return ops; +} + +IoOperations *udpTunnel_Create(Forwarder *forwarder, + const Address *localAddress, + const Address *remoteAddress) { + ListenerSet *set = forwarder_GetListenerSet(forwarder); + ListenerOps *listener = listenerSet_Find(set, ENCAP_UDP, localAddress); + IoOperations *ops = NULL; + if (listener) { + ops = udpTunnel_CreateOnListener(forwarder, listener, remoteAddress); + } else { + if (logger_IsLoggable(forwarder_GetLogger(forwarder), LoggerFacility_IO, + PARCLogLevel_Error)) { + char *str = addressToString(localAddress); + logger_Log(forwarder_GetLogger(forwarder), LoggerFacility_IO, + PARCLogLevel_Error, __func__, + "Could not find listener to match address %s", str); + parcMemory_Deallocate((void **)&str); + } + } + return ops; +} diff --git a/hicn-light/src/io/udpTunnel.h b/hicn-light/src/io/udpTunnel.h new file mode 100755 index 000000000..a79ca4a4e --- /dev/null +++ b/hicn-light/src/io/udpTunnel.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file udpTunnel.h + * @brief Establish a tunnel to a remote system + * + */ + +#ifndef udpTunnel_h +#define udpTunnel_h + +#include +#include +#include +#include + +/** + */ +IoOperations *udpTunnel_CreateOnListener(Forwarder *forwarder, + ListenerOps *localListener, + const Address *remoteAddress); + +/** + */ +IoOperations *udpTunnel_Create(Forwarder *forwarder, + const Address *localAddress, + const Address *remoteAddress); + +#endif // udpTunnel_h diff --git a/hicn-light/src/messenger/CMakeLists.txt b/hicn-light/src/messenger/CMakeLists.txt new file mode 100755 index 000000000..92bc13b5b --- /dev/null +++ b/hicn-light/src/messenger/CMakeLists.txt @@ -0,0 +1,32 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/missiveDeque.h + ${CMAKE_CURRENT_SOURCE_DIR}/missive.h + ${CMAKE_CURRENT_SOURCE_DIR}/missiveType.h + ${CMAKE_CURRENT_SOURCE_DIR}/messenger.h + ${CMAKE_CURRENT_SOURCE_DIR}/messengerRecipient.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/messenger.c + ${CMAKE_CURRENT_SOURCE_DIR}/messengerRecipient.c + ${CMAKE_CURRENT_SOURCE_DIR}/missive.c + ${CMAKE_CURRENT_SOURCE_DIR}/missiveDeque.c +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/hicn-light/src/messenger/messenger.c b/hicn-light/src/messenger/messenger.c new file mode 100755 index 000000000..26c7a85e2 --- /dev/null +++ b/hicn-light/src/messenger/messenger.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * + * The messenger is contructued with a reference to the forwarder's dispatcher + * so it can schedule future events. When someone calls messenger_Send(...), it + * will put the message on a queue. If the queue was empty, it will scheudle + * itself to be run. By running the queue in a future dispatcher slice, it + * guarantees that there will be no re-entrant behavior between callers and + * message listeners. + * + * A recipient will receive a reference counted copy of the missive, so it must + * call + * {@link missive_Release} on it. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct messenger { + PARCArrayList *callbacklist; + Dispatcher *dispatcher; + MissiveDeque *eventQueue; + + PARCEventTimer *timerEvent; +}; + +static void messenger_Dequeue(int fd, PARCEventType which_event, + void *messengerVoidPtr); + +// ========================================= +// Public API + +Messenger *messenger_Create(Dispatcher *dispatcher) { + Messenger *messenger = parcMemory_AllocateAndClear(sizeof(Messenger)); + parcAssertNotNull(messenger, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Messenger)); + + // NULL destroyer because we're storing structures owned by the caller + messenger->dispatcher = dispatcher; + messenger->callbacklist = parcArrayList_Create(NULL); + messenger->eventQueue = missiveDeque_Create(); + + // creates the timer, but does not start it + messenger->timerEvent = + dispatcher_CreateTimer(dispatcher, false, messenger_Dequeue, messenger); + + return messenger; +} + +void messenger_Destroy(Messenger **messengerPtr) { + parcAssertNotNull(messengerPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*messengerPtr, + "Parameter must dereference to non-null pointer"); + + Messenger *messenger = *messengerPtr; + parcArrayList_Destroy(&messenger->callbacklist); + missiveDeque_Release(&messenger->eventQueue); + dispatcher_DestroyTimerEvent(messenger->dispatcher, &messenger->timerEvent); + parcMemory_Deallocate((void **)&messenger); + *messengerPtr = NULL; +} + +void messenger_Send(Messenger *messenger, Missive *missive) { + parcAssertNotNull(messenger, "Parameter messenger must be non-null"); + parcAssertNotNull(missive, "Parameter event must be non-null"); + + missiveDeque_Append(messenger->eventQueue, missive); + if (missiveDeque_Size(messenger->eventQueue) == 1) { + // We need to scheudle ourself when an event is added to an empty queue + + // precondition: timer should not be running. + struct timeval immediateTimeout = {0, 0}; + dispatcher_StartTimer(messenger->dispatcher, messenger->timerEvent, + &immediateTimeout); + } +} + +static void removeRecipient(Messenger *messenger, + const MessengerRecipient *recipient) { + // don't increment i in the loop + for (size_t i = 0; i < parcArrayList_Size(messenger->callbacklist);) { + const void *p = parcArrayList_Get(messenger->callbacklist, i); + if (p == recipient) { + // removing will compact the list, so next element will also be at i. + parcArrayList_RemoveAndDestroyAtIndex(messenger->callbacklist, i); + } else { + i++; + } + } +} + +/** + * @function eventMessenger_Register + * @abstract Receive all event messages + */ +void messenger_Register(Messenger *messenger, + const MessengerRecipient *recipient) { + parcAssertNotNull(messenger, "Parameter messenger must be non-null"); + parcAssertNotNull(recipient, "Parameter recipient must be non-null"); + + // do not allow duplicates + removeRecipient(messenger, recipient); + + parcArrayList_Add(messenger->callbacklist, recipient); +} + +/** + * @function eventMessenger_Unregister + * @abstract Stop receiving event messages + */ +void messenger_Unregister(Messenger *messenger, + const MessengerRecipient *recipient) { + parcAssertNotNull(messenger, "Parameter messenger must be non-null"); + parcAssertNotNull(recipient, "Parameter recipient must be non-null"); + + removeRecipient(messenger, recipient); +} + +/** + * Called by event scheduler to give us a slice in which to dequeue events + * + * Called inside an event callback, so we now have exclusive access to the + * system. Dequeues all pending events and calls all the listeners for each one. + * + * @param [in] fd unused, required for compliance with function prototype + * @param [in] which_event unused, required for compliance with function + * prototype + * @param [in] messengerVoidPtr A void* to Messenger + */ +static void messenger_Dequeue(int fd, PARCEventType which_event, + void *messengerVoidPtr) { + Messenger *messenger = (Messenger *)messengerVoidPtr; + parcAssertNotNull(messenger, "Called with null messenger pointer"); + + Missive *missive; + while ((missive = missiveDeque_RemoveFirst(messenger->eventQueue)) != NULL) { + for (size_t i = 0; i < parcArrayList_Size(messenger->callbacklist); i++) { + MessengerRecipient *recipient = + parcArrayList_Get(messenger->callbacklist, i); + parcAssertTrue(recipient, "Recipient is null at index %zu", i); + + messengerRecipient_Deliver(recipient, missive_Acquire(missive)); + } + + // now let go of our reference to the missive + missive_Release(&missive); + } +} diff --git a/hicn-light/src/messenger/messenger.h b/hicn-light/src/messenger/messenger.h new file mode 100755 index 000000000..f945e7e72 --- /dev/null +++ b/hicn-light/src/messenger/messenger.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * The EventMessenger is the system that messages events between + * producers and consumers. + * + * Events are delivered in a deferred event cycle to avoid event callbacks + * firing when the event generator is still running. + */ + +#ifndef messenger_h +#define messenger_h + +#include +#include +#include + +struct messenger; +typedef struct messenger Messenger; + +/** + * @function eventmessenger_Create + * @abstract Creates an event notification system + * @discussion + * Typically there's only one of these managed by forwarder. + * + * @param dispatcher is the event dispatcher to use to schedule events. + */ +Messenger *messenger_Create(Dispatcher *dispatcher); + +/** + * @function eventMessenger_Destroy + * @abstract Destroys the messenger system, no notification is sent + */ +void messenger_Destroy(Messenger **messengerPtr); + +/** + * @function eventMessenger_Send + * @abstract Send an event message, takes ownership of the event memory + */ +void messenger_Send(Messenger *messenger, Missive *missive); + +/** + * @function eventMessenger_Register + * @abstract Receive all event messages + */ +void messenger_Register(Messenger *messenger, + const MessengerRecipient *recipient); + +/** + * @function eventMessenger_Unregister + * @abstract Stop receiving event messages + */ +void messenger_Unregister(Messenger *messenger, + const MessengerRecipient *recipient); +#endif // messenger_h diff --git a/hicn-light/src/messenger/messengerRecipient.c b/hicn-light/src/messenger/messengerRecipient.c new file mode 100755 index 000000000..14251f8eb --- /dev/null +++ b/hicn-light/src/messenger/messengerRecipient.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include + +struct messenger_recipient { + void *context; + MessengerRecipientCallback *notify; +}; + +MessengerRecipient *messengerRecipient_Create( + void *recipientContext, MessengerRecipientCallback *recipientCallback) { + parcAssertNotNull(recipientCallback, + "Parameter recipientCallback must be non-null"); + + MessengerRecipient *recipient = + parcMemory_AllocateAndClear(sizeof(MessengerRecipient)); + parcAssertNotNull(recipient, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(MessengerRecipient)); + recipient->context = recipientContext; + recipient->notify = recipientCallback; + return recipient; +} + +void messengerRecipient_Destroy(MessengerRecipient **recipientPtr) { + parcAssertNotNull(recipientPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*recipientPtr, + "Parameter must dereference to non-null pointer"); + + parcMemory_Deallocate((void **)recipientPtr); + *recipientPtr = NULL; +} + +void *messengerRecipient_GetRecipientContext(MessengerRecipient *recipient) { + parcAssertNotNull(recipient, "Parameter must be non-null"); + + return recipient->context; +} + +void messengerRecipient_Deliver(MessengerRecipient *recipient, + Missive *missive) { + parcAssertNotNull(recipient, "Parameter must be non-null"); + recipient->notify(recipient, missive); +} diff --git a/hicn-light/src/messenger/messengerRecipient.h b/hicn-light/src/messenger/messengerRecipient.h new file mode 100755 index 000000000..66d8f40f5 --- /dev/null +++ b/hicn-light/src/messenger/messengerRecipient.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file messengerRecipient.h + * @brief A recipient represents the entity that will recieve a Missive from the + * Messenger. + * + * A recipient is identified by the pair (contenxt, callback). The context is + * the recipients context, such as it's object pointer. The callback is the + * function the recipient uses to receive a Missive. + * + * If the receiver is going to do a lot of work or potentially send other + * missives, the receiver should queue the received notifications and process + * them in its own slice. + * + * A recipient will receive a reference counted copy of the missive, so it must + * call + * {@link missive_Release} on it. + * + * + */ + +#ifndef messengerRecipient_h +#define messengerRecipient_h + +#include + +struct messenger_recipient; +typedef struct messenger_recipient MessengerRecipient; + +/** + * @typedef MessengerRecipientCallback + * @abstract A recipient implements a callback to receive Missives. + * @constant recipient The recipient to recieve the missive + * @constant missive The missive, recipient must call {@link missive_Release} on + * it + */ +typedef void(MessengerRecipientCallback)(MessengerRecipient *recipient, + Missive *missive); + +/** + * Creates a Recipient, which represents a reciever of missives. + * + * Creates a Recipient that can be registerd with the Messenger using {@link + * messenger_Register}. + * + * @param [in] recipientContext This pointer will be passed back to the + * recipient with each missive, may be NULL + * @param [in] recipientCallback The function that receives the missive, must be + * non-NULL. + * + * @return non-null A recipient object + */ +MessengerRecipient *messengerRecipient_Create( + void *recipientContext, MessengerRecipientCallback *recipientCallback); + +/** + * Destroys a recipient. You should unregister it first. + * + * Destroying a recipient does not unregister it, so be sure to call + * {@link messenger_Unregister} first. + * + * @param [in,out] recipientPtr Double pointer to the recipient to destroy, will + * be NULL'd. + */ +void messengerRecipient_Destroy(MessengerRecipient **recipientPtr); + +/** + * Returns the recipient context passed on Create + * + * @param [in] recipient The recipient object + * + * @return pointer The context pointer used to create the object, maybe NULL + */ +void *messengerRecipient_GetRecipientContext(MessengerRecipient *recipient); + +/** + * Delivers a Missive to the recipient + * + * Passes the missive to the recipients callback. + * + * A recipient will receive a reference counted copy of the missive, so it must + * call + * {@link missive_Release} on it. + * + * @param [in] recipient The receiver + * @param [in] missive The message to send + */ +void messengerRecipient_Deliver(MessengerRecipient *recipient, + Missive *missive); +#endif // messengerRecipient_h diff --git a/hicn-light/src/messenger/missive.c b/hicn-light/src/messenger/missive.c new file mode 100755 index 000000000..a8bcb0282 --- /dev/null +++ b/hicn-light/src/messenger/missive.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include + +struct missive { + MissiveType missiveType; + unsigned connectionid; +}; + +parcObject_Override(Missive, PARCObject, .isLockable = false); + +Missive *missive_Create(MissiveType missiveType, unsigned connectionid) { + Missive *missive = parcObject_CreateInstance(Missive); + missive->missiveType = missiveType; + missive->connectionid = connectionid; + return missive; +} + +Missive *missive_Acquire(const Missive *missive) { + return parcObject_Acquire(missive); +} + +void missive_Release(Missive **missivePtr) { + parcObject_Release((void **)missivePtr); +} + +MissiveType missive_GetType(const Missive *missive) { + parcAssertNotNull(missive, "Parameter missive must be non-null"); + return missive->missiveType; +} + +unsigned missive_GetConnectionId(const Missive *missive) { + parcAssertNotNull(missive, "Parameter missive must be non-null"); + return missive->connectionid; +} diff --git a/hicn-light/src/messenger/missive.h b/hicn-light/src/messenger/missive.h new file mode 100755 index 000000000..33f3ef8b8 --- /dev/null +++ b/hicn-light/src/messenger/missive.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file missive.h + * @brief A Missive is a status message sent over a broadcast channel inside + * hicn-light + * + * Recipients use {@link messenger_Register} to receive missives. They are + * broadcast to all recipients. + * + */ +#ifndef missive_h +#define missive_h + +#include + +struct missive; +typedef struct missive Missive; + +/** + * Creates a Missive and sets the reference count to 1 + * + * A Missive may be sent to listeners of the Messenger to inform them of events + * on a connection id. + * + * @param [in] MissiveType The event type + * @param [in] connectionid The relevant conneciton id + * + * @return non-null A message + * @retrun null An error + */ +Missive *missive_Create(MissiveType missiveType, unsigned connectionid); + +/** + * Acquire a reference counted copy + * + * Increases the reference count by 1 and returns the original object. + * + * @param [in] missive An allocated missive + * + * @return non-null The original missive with increased reference count + */ +Missive *missive_Acquire(const Missive *missive); + +/** + * Releases a reference counted copy. + * + * If it is the last reference, the missive is freed. + * + * @param [in,out] missivePtr Double pointer to a missive, will be nulled. + */ +void missive_Release(Missive **missivePtr); + +/** + * Returns the type of the missive + * + * Returns the type of event the missive represents + * + * @param [in] missive An allocated missive + * + * @return MissiveType The event type + */ +MissiveType missive_GetType(const Missive *missive); + +/** + * Returns the connection ID of the missive + * + * An event is usually associated with a connection id (i.e. the I/O channel + * that originaged the event). + * + * @param [in] missive An allocated missive + * + * @return number The relevant connection id. + */ +unsigned missive_GetConnectionId(const Missive *missive); +#endif // missive_h diff --git a/hicn-light/src/messenger/missiveDeque.c b/hicn-light/src/messenger/missiveDeque.c new file mode 100755 index 000000000..418027d7a --- /dev/null +++ b/hicn-light/src/messenger/missiveDeque.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * A type-safe wrapper for Missives around a {@link PARCDeque}. We only + * implement the subset of functions used. + * + */ + +#include +#include +#include +#include +#include + +#include +#include + +struct missive_deque { + PARCDeque *queue; +}; + +MissiveDeque *missiveDeque_Create(void) { + MissiveDeque *missiveDeque = + parcMemory_AllocateAndClear(sizeof(MissiveDeque)); + parcAssertNotNull(missiveDeque, + "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(MissiveDeque)); + missiveDeque->queue = parcDeque_Create(); + return missiveDeque; +} + +void missiveDeque_Release(MissiveDeque **dequePtr) { + parcAssertNotNull(dequePtr, "Double pointer must be non-null"); + parcAssertNotNull(*dequePtr, "Double pointer must dereference to non-null"); + MissiveDeque *missiveDeque = *dequePtr; + + // flush the queue + while (!parcDeque_IsEmpty(missiveDeque->queue)) { + Missive *missive = missiveDeque_RemoveFirst(missiveDeque); + missive_Release(&missive); + } + + parcDeque_Release(&missiveDeque->queue); + parcMemory_Deallocate((void **)&missiveDeque); + *dequePtr = NULL; +} + +MissiveDeque *missiveDeque_Append(MissiveDeque *deque, Missive *missive) { + parcAssertNotNull(deque, "Parameter deque must be non-null"); + parcAssertNotNull(missive, "Parameter missive must be non-null"); + + parcDeque_Append(deque->queue, missive); + return deque; +} + +Missive *missiveDeque_RemoveFirst(MissiveDeque *deque) { + parcAssertNotNull(deque, "Parameter deque must be non-null"); + return (Missive *)parcDeque_RemoveFirst(deque->queue); +} + +size_t missiveDeque_Size(const MissiveDeque *deque) { + parcAssertNotNull(deque, "Parameter deque must be non-null"); + return parcDeque_Size(deque->queue); +} diff --git a/hicn-light/src/messenger/missiveDeque.h b/hicn-light/src/messenger/missiveDeque.h new file mode 100755 index 000000000..c6f955ce0 --- /dev/null +++ b/hicn-light/src/messenger/missiveDeque.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file missiveDeque + * @brief Double ended queue of Missives + * + * Used to queue Missives. This is a type-safe wrapper around {@link PARCDeque} + * + */ + +#ifndef missiveDeque_h +#define missiveDeque_h + +struct missive_deque; + +typedef struct missive_deque MissiveDeque; + +/** + * Create a `PARCDeque` instance with the default element equals function. + * + * The queue is created with no elements. + * + * The default element equals function is used by the `parcDeque_Equals` + * function and simply compares the values using the `==` operator. Users that + * need more sophisticated comparisons of the elements need to supply their own + * function via the `parcDeque_CreateCustom` function. + * + * @return non-NULL A pointer to a PARCDeque instance. + */ +MissiveDeque *missiveDeque_Create(void); + +void missiveDeque_Release(MissiveDeque **dequePtr); + +/** + * Appends the missive to the queue, taking ownership of the memory + */ +MissiveDeque *missiveDeque_Append(MissiveDeque *deque, Missive *missive); + +Missive *missiveDeque_RemoveFirst(MissiveDeque *deque); + +size_t missiveDeque_Size(const MissiveDeque *deque); +#endif // missiveDeque_h diff --git a/hicn-light/src/messenger/missiveType.h b/hicn-light/src/messenger/missiveType.h new file mode 100755 index 000000000..b0d9c7704 --- /dev/null +++ b/hicn-light/src/messenger/missiveType.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file missiveType + * @brief Defines what a Missive represents + * + * Currently, missives only carry information about the state of a connection + * (created, up, down, closed, destroyed). + * + */ + +#ifndef missiveType_h +#define missiveType_h + +/** + * @typedef Represents the state of a connection + * @abstract CREATE is the initial state. UP & DOWN are recurrent states. + * CLOSED is transient. DESTROYED is the terminal state. + * @constant MissiveType_ConnectionCreate Connection created (new) + * @constant MissiveType_ConnectionUp Connection is active and passing + * data + * @constant MissiveType_ConnectionDown Connection is inactive and cannot + * pass data + * @constant MissiveType_ConnectionClosed Connection closed and will be + * destroyed + * @constant MissiveType_ConnectionDestroyed Connection destroyed + * @discussion State transitions: + * initial -> CREATE + * CREATE -> (UP | DOWN) + * UP -> (DOWN | DESTROYED) + * DOWN -> (UP | CLOSED | DESTROYED) + * CLOSED -> DESTROYED + * DESTROYED -> terminal + */ +typedef enum { + MissiveType_ConnectionCreate, + MissiveType_ConnectionUp, + MissiveType_ConnectionDown, + MissiveType_ConnectionClosed, + MissiveType_ConnectionDestroyed +} MissiveType; +#endif // missiveType_h diff --git a/hicn-light/src/platforms/CMakeLists.txt b/hicn-light/src/platforms/CMakeLists.txt new file mode 100755 index 000000000..fcb4282ba --- /dev/null +++ b/hicn-light/src/platforms/CMakeLists.txt @@ -0,0 +1,31 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +if(${CMAKE_SYSTEM_NAME} STREQUAL "Android") + list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/android/system.c + ) +elseif(APPLE) + list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/darwin/system.c + ) +elseif( ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" ) + list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/linux/system.c + ) +endif() + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/hicn-light/src/platforms/README.txt b/hicn-light/src/platforms/README.txt new file mode 100755 index 000000000..a1293944c --- /dev/null +++ b/hicn-light/src/platforms/README.txt @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +Operating system dependent modules. + diff --git a/hicn-light/src/platforms/android/system.c b/hicn-light/src/platforms/android/system.c new file mode 100755 index 000000000..68f99424b --- /dev/null +++ b/hicn-light/src/platforms/android/system.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +//#define __USE_MISC +#include + +// to get the list of arp types +#include + +// for the mac address +#include + +#include +#include + +#include + +#include "ifaddrs.h" + +/** + * Returns the MTU for a named interface + * + * On linux, we get the MTU by opening a socket and reading SIOCGIFMTU + * + * @param [in] ifname Interface name (e.g. "eth0") + * + * @retval number The MTU in bytes + * + * Example: + * @code + * <#example#> + * @endcode + */ +static int getMtu(const char *ifname) { + struct ifreq ifr; + int fd; + + fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + + strcpy(ifr.ifr_name, ifname); + ioctl(fd, SIOCGIFMTU, &ifr); + + close(fd); + return ifr.ifr_mtu; +} + +InterfaceSet *system_Interfaces(Forwarder *forwarder) { + InterfaceSet *set = interfaceSetCreate(); + + Logger *logger = forwarder_GetLogger(forwarder); + + // this is the dynamically allocated head of the list + struct ifaddrs *ifaddr; + int failure = getifaddrs(&ifaddr); + parcAssertFalse(failure, "Error getifaddrs: (%d) %s", errno, strerror(errno)); + + struct ifaddrs *next; + for (next = ifaddr; next != NULL; next = next->ifa_next) { + if ((next->ifa_addr == NULL) || ((next->ifa_flags & IFF_UP) == 0)) { + continue; + } + + Interface *iface = interfaceSetGetByName(set, next->ifa_name); + if (iface == NULL) { + unsigned mtu = (unsigned)getMtu(next->ifa_name); + + iface = interfaceCreate( + next->ifa_name, forwarder_GetNextConnectionId(forwarder), + next->ifa_flags & IFF_LOOPBACK, next->ifa_flags & IFF_MULTICAST, mtu); + + interfaceSetAdd(set, iface); + } + + int family = next->ifa_addr->sa_family; + switch (family) { + case AF_INET: { + Address *address = + addressCreateFromInet((struct sockaddr_in *)next->ifa_addr); + interfaceAddAddress(iface, address); + break; + } + + case AF_INET6: { + Address *address = + addressCreateFromInet6((struct sockaddr_in6 *)next->ifa_addr); + interfaceAddAddress(iface, address); + break; + } + + case AF_PACKET: { + struct sockaddr_ll *addr_ll = (struct sockaddr_ll *)next->ifa_addr; + + if (logger_IsLoggable(logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + logger_Log(logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "sockaddr_ll family %d proto %d ifindex %d hatype %d " + "pkttype %d halen %d", + addr_ll->sll_family, addr_ll->sll_protocol, + addr_ll->sll_ifindex, addr_ll->sll_hatype, + addr_ll->sll_pkttype, addr_ll->sll_halen); + } + + switch (addr_ll->sll_hatype) { + // list of the ARP hatypes we can extract a MAC address from + case ARPHRD_ETHER: + // fallthrough + case ARPHRD_IEEE802: { + Address *address = addressCreateFromLink( + (uint8_t *)addr_ll->sll_addr, addr_ll->sll_halen); + interfaceAddAddress(iface, address); + break; + } + default: + break; + } + + break; + } + } + } + + freeifaddrs(ifaddr); + return set; +} + +Address *system_GetMacAddressByName(Forwarder *forwarder, + const char *interfaceName) { + Address *linkAddress = NULL; + + InterfaceSet *interfaceSet = system_Interfaces(forwarder); + Interface *interface = interfaceSetGetByName(interfaceSet, interfaceName); + + if (interface) { + const AddressList *addressList = interfaceGetAddresses(interface); + + size_t length = addressListLength(addressList); + for (size_t i = 0; i < length && !linkAddress; i++) { + const Address *a = addressListGetItem(addressList, i); + if (addressGetType(a) == ADDR_LINK) { + linkAddress = addressCopy(a); + } + } + } + + interfaceSetDestroy(&interfaceSet); + + return linkAddress; +} + +unsigned system_InterfaceMtu(Forwarder *forwarder, const char *interfaceName) { + unsigned mtu = 0; + + InterfaceSet *interfaceSet = system_Interfaces(forwarder); + Interface *interface = interfaceSetGetByName(interfaceSet, interfaceName); + + if (interface) { + mtu = interfaceGetMTU(interface); + } + + interfaceSetDestroy(&interfaceSet); + + return mtu; +} diff --git a/hicn-light/src/platforms/darwin/system.c b/hicn-light/src/platforms/darwin/system.c new file mode 100755 index 000000000..b8ef80c63 --- /dev/null +++ b/hicn-light/src/platforms/darwin/system.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include +#include + +InterfaceSet *system_Interfaces(Forwarder *forwarder) { + InterfaceSet *set = interfaceSetCreate(); + + // this is the dynamically allocated head of the list + struct ifaddrs *ifaddr; + int failure = getifaddrs(&ifaddr); + parcAssertFalse(failure, "Error getifaddrs: (%d) %s", errno, strerror(errno)); + + struct ifaddrs *next; + for (next = ifaddr; next != NULL; next = next->ifa_next) { + if ((next->ifa_addr == NULL) || ((next->ifa_flags & IFF_UP) == 0)) { + continue; + } + + // This assumes the LINK address comes first so we can get the MTU + // when the interface is created. + + Interface *iface = interfaceSetGetByName(set, next->ifa_name); + if (iface == NULL) { + unsigned mtu = 0; + + if (next->ifa_data != NULL) { + struct if_data *ifdata = (struct if_data *)next->ifa_data; + mtu = ifdata->ifi_mtu; + } + + iface = interfaceCreate( + next->ifa_name, forwarder_GetNextConnectionId(forwarder), + next->ifa_flags & IFF_LOOPBACK, next->ifa_flags & IFF_MULTICAST, mtu); + + interfaceSetAdd(set, iface); + } + + int family = next->ifa_addr->sa_family; + switch (family) { + case AF_INET: { + Address *address = + addressCreateFromInet((struct sockaddr_in *)next->ifa_addr); + interfaceAddAddress(iface, address); + break; + } + + case AF_INET6: { + Address *address = + addressCreateFromInet6((struct sockaddr_in6 *)next->ifa_addr); + interfaceAddAddress(iface, address); + break; + } + + case AF_LINK: { + struct sockaddr_dl *addr_dl = (struct sockaddr_dl *)next->ifa_addr; + + // skip links with 0-length address + if (addr_dl->sdl_alen > 0) { + // addr_dl->sdl_data[12] contains the interface name followed by the + // MAC address, so need to offset in to the array past the interface + // name. + Address *address = addressCreateFromLink( + (uint8_t *)&addr_dl->sdl_data[addr_dl->sdl_nlen], + addr_dl->sdl_alen); + interfaceAddAddress(iface, address); + } + break; + } + } + } + + freeifaddrs(ifaddr); + + return set; +} + +Address *system_GetMacAddressByName(Forwarder *forwarder, + const char *interfaceName) { + Address *linkAddress = NULL; + + InterfaceSet *interfaceSet = system_Interfaces(forwarder); + Interface *interface = interfaceSetGetByName(interfaceSet, interfaceName); + + if (interface) { + const AddressList *addressList = interfaceGetAddresses(interface); + + size_t length = addressListLength(addressList); + for (size_t i = 0; i < length && !linkAddress; i++) { + const Address *a = addressListGetItem(addressList, i); + if (addressGetType(a) == ADDR_LINK) { + linkAddress = addressCopy(a); + } + } + } + + interfaceSetDestroy(&interfaceSet); + + return linkAddress; +} + +unsigned system_InterfaceMtu(Forwarder *forwarder, const char *interfaceName) { + unsigned mtu = 0; + + if (interfaceName) { + InterfaceSet *interfaceSet = system_Interfaces(forwarder); + Interface *interface = interfaceSetGetByName(interfaceSet, interfaceName); + + if (interface) { + mtu = interfaceGetMTU(interface); + } + + interfaceSetDestroy(&interfaceSet); + } + return mtu; +} diff --git a/hicn-light/src/platforms/linux/system.c b/hicn-light/src/platforms/linux/system.c new file mode 100755 index 000000000..fcf13becc --- /dev/null +++ b/hicn-light/src/platforms/linux/system.c @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define __USE_MISC +#include + +// to get the list of arp types +#include + +// for the mac address +#include + +#include +#include + +#include + +#include + +/** + * Returns the MTU for a named interface + * + * On linux, we get the MTU by opening a socket and reading SIOCGIFMTU + * + * @param [in] ifname Interface name (e.g. "eth0") + * + * @retval number The MTU in bytes + * + * Example: + * @code + * <#example#> + * @endcode + */ +static int getMtu(const char *ifname) { + struct ifreq ifr; + int fd; + + fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + + strcpy(ifr.ifr_name, ifname); + ioctl(fd, SIOCGIFMTU, &ifr); + + close(fd); + return ifr.ifr_mtu; +} + +InterfaceSet *system_Interfaces(Forwarder *forwarder) { + InterfaceSet *set = interfaceSetCreate(); + + Logger *logger = forwarder_GetLogger(forwarder); + + // this is the dynamically allocated head of the list + struct ifaddrs *ifaddr; + int failure = getifaddrs(&ifaddr); + parcAssertFalse(failure, "Error getifaddrs: (%d) %s", errno, strerror(errno)); + + struct ifaddrs *next; + for (next = ifaddr; next != NULL; next = next->ifa_next) { + if ((next->ifa_addr == NULL) || ((next->ifa_flags & IFF_UP) == 0)) { + continue; + } + + Interface *iface = interfaceSetGetByName(set, next->ifa_name); + if (iface == NULL) { + unsigned mtu = (unsigned)getMtu(next->ifa_name); + + iface = interfaceCreate( + next->ifa_name, forwarder_GetNextConnectionId(forwarder), + next->ifa_flags & IFF_LOOPBACK, next->ifa_flags & IFF_MULTICAST, mtu); + + interfaceSetAdd(set, iface); + } + + int family = next->ifa_addr->sa_family; + switch (family) { + case AF_INET: { + Address *address = + addressCreateFromInet((struct sockaddr_in *)next->ifa_addr); + interfaceAddAddress(iface, address); + break; + } + + case AF_INET6: { + Address *address = + addressCreateFromInet6((struct sockaddr_in6 *)next->ifa_addr); + interfaceAddAddress(iface, address); + break; + } + + case AF_PACKET: { + struct sockaddr_ll *addr_ll = (struct sockaddr_ll *)next->ifa_addr; + + if (logger_IsLoggable(logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + logger_Log(logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "sockaddr_ll family %d proto %d ifindex %d hatype %d " + "pkttype %d halen %d", + addr_ll->sll_family, addr_ll->sll_protocol, + addr_ll->sll_ifindex, addr_ll->sll_hatype, + addr_ll->sll_pkttype, addr_ll->sll_halen); + } + + switch (addr_ll->sll_hatype) { + // list of the ARP hatypes we can extract a MAC address from + case ARPHRD_ETHER: + // fallthrough + case ARPHRD_IEEE802: { + Address *address = addressCreateFromLink( + (uint8_t *)addr_ll->sll_addr, addr_ll->sll_halen); + interfaceAddAddress(iface, address); + break; + } + default: + break; + } + + break; + } + } + } + + freeifaddrs(ifaddr); + return set; +} + +Address *system_GetMacAddressByName(Forwarder *forwarder, + const char *interfaceName) { + Address *linkAddress = NULL; + + InterfaceSet *interfaceSet = system_Interfaces(forwarder); + Interface *interface = interfaceSetGetByName(interfaceSet, interfaceName); + + if (interface) { + const AddressList *addressList = interfaceGetAddresses(interface); + + size_t length = addressListLength(addressList); + for (size_t i = 0; i < length && !linkAddress; i++) { + const Address *a = addressListGetItem(addressList, i); + if (addressGetType(a) == ADDR_LINK) { + linkAddress = addressCopy(a); + } + } + } + + interfaceSetDestroy(&interfaceSet); + + return linkAddress; +} + +unsigned system_InterfaceMtu(Forwarder *forwarder, const char *interfaceName) { + unsigned mtu = 0; + + InterfaceSet *interfaceSet = system_Interfaces(forwarder); + Interface *interface = interfaceSetGetByName(interfaceSet, interfaceName); + + if (interface) { + mtu = interfaceGetMTU(interface); + } + + interfaceSetDestroy(&interfaceSet); + + return mtu; +} diff --git a/hicn-light/src/processor/CMakeLists.txt b/hicn-light/src/processor/CMakeLists.txt new file mode 100755 index 000000000..b7eeabe3b --- /dev/null +++ b/hicn-light/src/processor/CMakeLists.txt @@ -0,0 +1,40 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/fibEntry.h + ${CMAKE_CURRENT_SOURCE_DIR}/fibEntryList.h + ${CMAKE_CURRENT_SOURCE_DIR}/messageProcessor.h + ${CMAKE_CURRENT_SOURCE_DIR}/hashTableFunction.h + ${CMAKE_CURRENT_SOURCE_DIR}/pit.h + ${CMAKE_CURRENT_SOURCE_DIR}/fib.h + ${CMAKE_CURRENT_SOURCE_DIR}/pitEntry.h + ${CMAKE_CURRENT_SOURCE_DIR}/pitVerdict.h + ${CMAKE_CURRENT_SOURCE_DIR}/pitStandard.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/hashTableFunction.c + ${CMAKE_CURRENT_SOURCE_DIR}/fib.c + ${CMAKE_CURRENT_SOURCE_DIR}/fibEntry.c + ${CMAKE_CURRENT_SOURCE_DIR}/fibEntryList.c + ${CMAKE_CURRENT_SOURCE_DIR}/messageProcessor.c + ${CMAKE_CURRENT_SOURCE_DIR}/pit.c + ${CMAKE_CURRENT_SOURCE_DIR}/pitEntry.c + ${CMAKE_CURRENT_SOURCE_DIR}/pitStandard.c +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) diff --git a/hicn-light/src/processor/fib.c b/hicn-light/src/processor/fib.c new file mode 100755 index 000000000..33d31fd8a --- /dev/null +++ b/hicn-light/src/processor/fib.c @@ -0,0 +1,448 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include +#include + +#include + +#define NULL_POS 128 +#define MSB_POS 127 + +struct node; +typedef struct node FibNode; + +struct node { + FibNode *left; + FibNode *right; + FibEntry *entry; + unsigned pos; +}; + +struct fib { + FibNode *root; + unsigned size; +}; + +// ===================================================== +// Public API + +FibNode *_createNode(FibNode *left, FibNode *right, FibEntry *entry, + unsigned pos) { + FibNode *n = parcMemory_AllocateAndClear(sizeof(FibNode)); + parcAssertNotNull(n, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(FibNode)); + + n->left = left; + n->right = right; + n->entry = entry; + n->pos = pos; + + return n; +} + +FIB *fib_Create() { + FIB *hicnFib = parcMemory_AllocateAndClear(sizeof(FIB)); + parcAssertNotNull(hicnFib, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(FIB)); + + hicnFib->root = + _createNode(NULL, NULL, NULL, + NULL_POS); // the pos will decrease going down in the trie + hicnFib->root->left = hicnFib->root; + hicnFib->root->right = hicnFib->root; + + hicnFib->size = 0; + + return hicnFib; +} + +void _destroyNode(FibNode *n) { + fibEntry_Release(&n->entry); + parcMemory_Deallocate((void **)&n); + n = NULL; +} + +void _destroyFib(FIB *fib) { + // XXX + // to be done + return; +} + +void fib_Destroy(FIB **fibPtr) { + parcAssertNotNull(fibPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*fibPtr, "Parameter must dereference to non-null pointer"); + + FIB *fib = *fibPtr; + + _destroyFib(fib); + parcMemory_Deallocate((void **)&fib); + *fibPtr = NULL; +} + +void fib_Add(FIB *fib, FibEntry *entry) { + parcAssertNotNull(fib, "Parameter must be non-null"); + parcAssertNotNull(entry, "Parameter must be non-null"); + + NameBitvector *name = name_GetContentName(fibEntry_GetPrefix(entry)); + + // search the name + FibNode *prev = fib->root; + FibNode *curr; + + if (nameBitvector_testBit(name, MSB_POS)) { + curr = fib->root->right; + } else { + curr = fib->root->left; + } + + while (prev->pos > curr->pos) { + prev = curr; + if (nameBitvector_testBit(name, curr->pos)) { + curr = curr->right; + } else { + curr = curr->left; + } + } + + if (curr->entry != NULL && + nameBitvector_Equals( + name, name_GetContentName(fibEntry_GetPrefix(curr->entry)))) { + // there is already an entry with this name + // do nothing. Before call ADD we should check + // if the node exists, and, in that case update it + return; + } + + // if the name is not in the FIB search for the first different bit between + // the new name to add and the node found in the trie + uint8_t pos = MSB_POS; + if (curr->entry != NULL) + pos = nameBitvector_firstDiff( + name, name_GetContentName(fibEntry_GetPrefix(curr->entry))); + + // reset pointer and search the insertion point + prev = fib->root; + if (nameBitvector_testBit(name, MSB_POS)) + curr = fib->root->right; + else + curr = fib->root->left; + + while (prev->pos > curr->pos && curr->pos > pos) { + prev = curr; + if (nameBitvector_testBit(name, curr->pos)) { + curr = curr->right; + } else { + curr = curr->left; + } + } + + // insert the node + fib->size++; + FibNode *n = _createNode(NULL, NULL, entry, pos); + + if (nameBitvector_testBit(name, pos)) { + n->left = curr; + n->right = n; + } else { + n->left = n; + n->right = curr; + } + + uint8_t new_pos = prev->pos; + if (new_pos == NULL_POS) new_pos = MSB_POS; + + if (nameBitvector_testBit(name, new_pos)) { + prev->right = n; + } else { + prev->left = n; + } +} + +FibEntry *fib_Contains(const FIB *fib, const Name *prefix) { + parcAssertNotNull(fib, "Parameter must be non-null"); + parcAssertNotNull(prefix, "Parameter must be non-null"); + + NameBitvector *name = name_GetContentName(prefix); + + // this is the same as the first part of the add function + // we cannnot call this function inside the add because + // we need the pointer prev and curr for the insertion + + FibNode *prev = fib->root; + FibNode *curr; + + if (nameBitvector_testBit(name, MSB_POS)) + curr = fib->root->right; + else + curr = fib->root->left; + + while (prev->pos > curr->pos) { + prev = curr; + + if (nameBitvector_testBit(name, curr->pos)) { + curr = curr->right; + } else { + curr = curr->left; + } + } + + if (curr->entry != NULL && + nameBitvector_Equals( + name, name_GetContentName(fibEntry_GetPrefix(curr->entry)))) { + return curr->entry; + } else { + return NULL; + } +} + +void _removeNode(FIB *fib, const Name *prefix) { + parcAssertNotNull(fib, "Parameter must be non-null"); + parcAssertNotNull(prefix, "Parameter must be non-null"); + + FibNode *grand = NULL; // grandparent + FibNode *prev = + fib->root; // parent: it will points to curr of the next hop in the trie + FibNode *curr; // current node: the node to remove + + NameBitvector *name = name_GetContentName(prefix); + + if (nameBitvector_testBit(name, MSB_POS)) { + curr = fib->root->right; + } else { + curr = fib->root->left; + } + + // in the first loop we always search the node to remove + while (prev->pos > curr->pos) { + grand = prev; + prev = curr; + + if (nameBitvector_testBit(name, curr->pos)) { + curr = curr->right; + } else { + curr = curr->left; + } + } + + if (!nameBitvector_Equals( + name, name_GetContentName(fibEntry_GetPrefix(curr->entry)))) { + // the node does not exists + return; + } + + // search for the real parent of curr (*tmpPrev) + // prev points to curr or next node in the trie + // this is because of the loopback links + + FibNode *tmpPrev = fib->root; + FibNode *tmpCurr; + + if (nameBitvector_testBit(name, MSB_POS)) { + tmpCurr = fib->root->right; + } else { + tmpCurr = fib->root->left; + } + + // here we compare pointer so we are sure to stop at the right potion + while (tmpCurr != curr) { + tmpPrev = tmpCurr; + + if (nameBitvector_testBit(name, tmpCurr->pos)) { + tmpCurr = tmpCurr->right; + } else { + tmpCurr = tmpCurr->left; + } + } + + // now curr is the node to remove and tmpPrev is the real parent of curr + + if (curr == prev) { + // this is the case where curr is a leaf node + FibNode *next; // child of curr (the loopback) + + if (nameBitvector_testBit(name, curr->pos)) { + next = curr->left; + } else { + next = curr->right; + } + + if (nameBitvector_testBit(name, tmpPrev->pos)) { + tmpPrev->right = next; + } else { + tmpPrev->left = next; + } + + } else { + // curr is an internal node + FibNode *next; // child of prev (loopback) + + if (nameBitvector_testBit(name, prev->pos)) { + next = prev->left; + } else { + next = prev->right; + } + + if (nameBitvector_testBit(name, grand->pos)) { + grand->right = next; + } else { + grand->left = next; + } + + if (nameBitvector_testBit(name, tmpPrev->pos)) { + tmpPrev->right = prev; + } else { + tmpPrev->left = prev; + } + + prev->left = curr->left; + prev->right = curr->right; + prev->pos = curr->pos; + } + + fib->size--; + _destroyNode(curr); +} + +void fib_Remove(FIB *fib, const Name *name, unsigned connId) { + parcAssertNotNull(fib, "Parameter must be non-null"); + parcAssertNotNull(name, "Parameter must be non-null"); + + FibEntry *entry = fib_Contains(fib, name); + + if (entry == NULL) { + return; + } + + fibEntry_RemoveNexthopByConnectionId(entry, connId); + if (fibEntry_NexthopCount(entry) == 0) { + _removeNode(fib, name); + } +} + +void _removeConnectionId(FibNode *n, unsigned pos, unsigned connectionId, + FibEntryList *list) { + if (n->pos < pos) { + fibEntry_RemoveNexthopByConnectionId(n->entry, connectionId); + if (fibEntry_NexthopCount(n->entry) == 0) { + fibEntryList_Append(list, n->entry); + } + _removeConnectionId(n->left, n->pos, connectionId, list); + _removeConnectionId(n->right, n->pos, connectionId, list); + } +} + +void fib_RemoveConnectionId(FIB *fib, unsigned connectionId) { + parcAssertNotNull(fib, "Parameter must be non-null"); + + // 1 - we vist the tree to remove the connection id + // 2 - during the visit we collect the fib entry with 0 nexthop + // 3 - after the visit we remove this entries + + FibEntryList *list = fibEntryList_Create(); + + _removeConnectionId(fib->root->left, fib->root->pos, connectionId, list); + _removeConnectionId(fib->root->right, fib->root->pos, connectionId, list); + + for (int i = 0; i < fibEntryList_Length(list); i++) { + _removeNode(fib, fibEntry_GetPrefix(fibEntryList_Get(list, i))); + } + + fibEntryList_Destroy(&list); +} + +size_t fib_Length(const FIB *fib) { + parcAssertNotNull(fib, "Parameter must be non-null"); + return fib->size; +} + +FibEntry *fib_Match(const FIB *fib, const Message *interestMessage) { + parcAssertNotNull(fib, "Parameter must be non-null"); + parcAssertNotNull(interestMessage, "Parameter must be non-null"); + + NameBitvector *name = name_GetContentName(message_GetName(interestMessage)); + + FibNode *prev = fib->root; + FibNode *curr; + + FibNode *match = NULL; + unsigned len = 0; + + if (nameBitvector_testBit(name, MSB_POS)) + curr = fib->root->right; + else + curr = fib->root->left; + + while (prev->pos > curr->pos) { + prev = curr; + + if (curr->entry != NULL) { + if (nameBitvector_StartsWith( + name, name_GetContentName(fibEntry_GetPrefix(curr->entry))) && + nameBitvector_GetLength( + name_GetContentName(fibEntry_GetPrefix(curr->entry))) > len) { + match = curr; + len = nameBitvector_GetLength( + name_GetContentName(fibEntry_GetPrefix(curr->entry))); + } + } + + if (nameBitvector_testBit(name, curr->pos)) + curr = curr->right; + else + curr = curr->left; + } + + if (curr->entry != NULL) { + if (nameBitvector_StartsWith( + name, name_GetContentName(fibEntry_GetPrefix(curr->entry))) && + nameBitvector_GetLength( + name_GetContentName(fibEntry_GetPrefix(curr->entry))) > len) { + match = curr; + len = nameBitvector_GetLength( + name_GetContentName(fibEntry_GetPrefix(curr->entry))); + } + } + + if (match != NULL && match->entry != NULL) { + return match->entry; + } else { + return NULL; + } +} + +void _collectFibEntries(FibNode *n, int pos, FibEntryList *list) { + if (n->pos < pos) { + fibEntryList_Append(list, n->entry); + _collectFibEntries(n->left, n->pos, list); + _collectFibEntries(n->right, n->pos, list); + } +} + +FibEntryList *fib_GetEntries(const FIB *fib) { + parcAssertNotNull(fib, "Parameter must be non-null"); + + FibEntryList *list = fibEntryList_Create(); + + _collectFibEntries(fib->root->left, fib->root->pos, list); + _collectFibEntries(fib->root->right, fib->root->pos, list); + + return list; +} diff --git a/hicn-light/src/processor/fib.h b/hicn-light/src/processor/fib.h new file mode 100755 index 000000000..4409419db --- /dev/null +++ b/hicn-light/src/processor/fib.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef fib_h +#define fib_h + +#include +#include +#include +#include + +struct fib; +typedef struct fib FIB; + +FIB *fib_Create(); + +void fib_Destroy(FIB **fibPtr); + +void fib_Add(FIB *fib, FibEntry *node); + +FibEntry *fib_Contains(const FIB *fib, const Name *prefix); + +void fib_Remove(FIB *fib, const Name *prefix, unsigned connId); + +void fib_RemoveConnectionId(FIB *fib, unsigned connectionId); + +FibEntry *fib_Match(const FIB *fib, const Message *interestMessage); + +size_t fib_Length(const FIB *fib); + +FibEntryList *fib_GetEntries(const FIB *fib); +#endif // fib_h diff --git a/hicn-light/src/processor/fibEntry.c b/hicn-light/src/processor/fibEntry.c new file mode 100755 index 000000000..bb877030f --- /dev/null +++ b/hicn-light/src/processor/fibEntry.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#ifdef WITH_MAPME +#include +#include +#endif /* WITH_MAPME */ + +#include +#include + +#include + +struct fib_entry { + Name *name; + unsigned refcount; + StrategyImpl *fwdStrategy; +#ifdef WITH_MAPME + void *userData; + void (*userDataRelease)(void **userData); +#endif /* WITH_MAPME */ +}; + +FibEntry *fibEntry_Create(Name *name, strategy_type fwdStrategy) { + FibEntry *fibEntry = parcMemory_AllocateAndClear(sizeof(FibEntry)); + parcAssertNotNull(fibEntry, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(FibEntry)); + fibEntry->name = name_Acquire(name); + + if (fwdStrategy) { + switch (fwdStrategy) { + case SET_STRATEGY_LOADBALANCER: + fibEntry->fwdStrategy = strategyLoadBalancer_Create(); + break; + + case SET_STRATEGY_RANDOM_PER_DASH_SEGMENT: + fibEntry->fwdStrategy = strategyRndSegment_Create(); + break; + + case SET_STRATEGY_LOADBALANCER_WITH_DELAY: + fibEntry->fwdStrategy = strategyLoadBalancerWithPD_Create(); + break; + + default: + // LB is the defualt strategy + fibEntry->fwdStrategy = strategyLoadBalancer_Create(); + // the LB strategy is the default one + // other strategies can be set using the appropiate function + break; + } + + } else { + fibEntry->fwdStrategy = strategyLoadBalancer_Create(); + } + + fibEntry->refcount = 1; + +#ifdef WITH_MAPME + fibEntry->userData = NULL; + fibEntry->userDataRelease = NULL; +#endif /* WITH_MAPME */ + + return fibEntry; +} + +FibEntry *fibEntry_Acquire(const FibEntry *fibEntry) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + FibEntry *copy = (FibEntry *)fibEntry; + copy->refcount++; + return copy; +} + +void fibEntry_Release(FibEntry **fibEntryPtr) { + FibEntry *fibEntry = *fibEntryPtr; + parcAssertTrue(fibEntry->refcount > 0, "Illegal state: refcount is 0"); + fibEntry->refcount--; + if (fibEntry->refcount == 0) { + name_Release(&fibEntry->name); + fibEntry->fwdStrategy->destroy(&(fibEntry->fwdStrategy)); +#ifdef WITH_MAPME + if (fibEntry->userData) { + fibEntry->userDataRelease(&fibEntry->userData); + } +#endif /* WITH_MAPME */ + parcMemory_Deallocate((void **)&fibEntry); + } + *fibEntryPtr = NULL; +} + +void fibEntry_SetStrategy(FibEntry *fibEntry, strategy_type strategy) { + StrategyImpl *fwdStrategyImpl; + + switch (strategy) { + case SET_STRATEGY_LOADBALANCER: + fwdStrategyImpl = strategyLoadBalancer_Create(); + break; + + case SET_STRATEGY_RANDOM_PER_DASH_SEGMENT: + fwdStrategyImpl = strategyRndSegment_Create(); + break; + + case SET_STRATEGY_LOADBALANCER_WITH_DELAY: + fwdStrategyImpl = strategyLoadBalancerWithPD_Create(); + break; + + default: + // LB is the defualt strategy + fwdStrategyImpl = strategyLoadBalancer_Create(); + // the LB strategy is the default one + // other strategies can be set using the appropiate function + break; + } + + const NumberSet *nexthops = fibEntry_GetNexthops(fibEntry); + unsigned size = fibEntry_NexthopCount(fibEntry); + for (unsigned i = 0; i < size; i++) { + fwdStrategyImpl->addNexthop(fwdStrategyImpl, + numberSet_GetItem(nexthops, i)); + } + fibEntry->fwdStrategy->destroy(&(fibEntry->fwdStrategy)); + fibEntry->fwdStrategy = fwdStrategyImpl; +} +void fibEntry_AddNexthop(FibEntry *fibEntry, unsigned connectionId) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + fibEntry->fwdStrategy->addNexthop(fibEntry->fwdStrategy, connectionId); +} + +void fibEntry_RemoveNexthopByConnectionId(FibEntry *fibEntry, + unsigned connectionId) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + fibEntry->fwdStrategy->removeNexthop(fibEntry->fwdStrategy, connectionId); +} + +size_t fibEntry_NexthopCount(const FibEntry *fibEntry) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + return fibEntry->fwdStrategy->countNexthops(fibEntry->fwdStrategy); +} + +const NumberSet *fibEntry_GetNexthops(const FibEntry *fibEntry) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + return fibEntry->fwdStrategy->returnNexthops(fibEntry->fwdStrategy); +} + +const NumberSet *fibEntry_GetNexthopsFromForwardingStrategy( + const FibEntry *fibEntry, const Message *interestMessage) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + return fibEntry->fwdStrategy->lookupNexthop(fibEntry->fwdStrategy, + interestMessage); +} + +void fibEntry_ReceiveObjectMessage(const FibEntry *fibEntry, + const NumberSet *egressId, + const Message *objectMessage, Ticks rtt) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + fibEntry->fwdStrategy->receiveObject(fibEntry->fwdStrategy, egressId, + objectMessage, rtt); +} + +void fibEntry_OnTimeout(const FibEntry *fibEntry, const NumberSet *egressId) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + fibEntry->fwdStrategy->onTimeout(fibEntry->fwdStrategy, egressId); +} + +Name *fibEntry_GetPrefix(const FibEntry *fibEntry) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + return fibEntry->name; + // return metisName_Acquire(fibEntry->name); +} + +strategy_type fibEntry_GetFwdStrategyType(const FibEntry *fibEntry) { + return fibEntry->fwdStrategy->getStrategy(fibEntry->fwdStrategy); +} + +StrategyImpl *fibEntry_GetFwdStrategy(const FibEntry *fibEntry) { + return fibEntry->fwdStrategy; +} + +#ifdef WITH_MAPME + +void fibEntry_AddNexthopByConnectionId(FibEntry *fibEntry, + unsigned connectionId) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + fibEntry->fwdStrategy->addNexthop(fibEntry->fwdStrategy, connectionId); +} + +void *fibEntry_getUserData(const FibEntry *fibEntry) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + return fibEntry->userData; +} + +void fibEntry_setUserData(FibEntry *fibEntry, const void *userData, + void (*userDataRelease)(void **)) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + fibEntry->userData = (void *)userData; + fibEntry->userDataRelease = userDataRelease; +} + +#endif /* WITH_MAPME */ diff --git a/hicn-light/src/processor/fibEntry.h b/hicn-light/src/processor/fibEntry.h new file mode 100755 index 000000000..3bcac3884 --- /dev/null +++ b/hicn-light/src/processor/fibEntry.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file fibEntry.h + * @brief A forwarding entry in the FIB table + * + * A Forwarding Information Base (FIB) entry (FibEntry) is a + * set of nexthops for a name. It also indicates the forwarding strategy. + * + * Each nexthop contains the ConnectionId assocaited with it. This could be + * something specific like a MAC address or point-to-point tunnel. Or, it + * could be something general like a MAC group address or ip multicast overlay. + * + * See strategy.h for a description of forwarding strategies. + * In short, a strategy is the algorithm used to select one or more nexthops + * from the set of available nexthops. + * + * Each nexthop also contains a void* to a forwarding strategy data container. + * This allows a strategy to keep proprietary information about each nexthop. + * + * + */ + +#ifndef fibEntry_h +#define fibEntry_h + +#include +#include + +#ifdef WITH_MAPME +#include +#include +#endif /* WITH_MAPME */ + +struct fib_entry; +typedef struct fib_entry FibEntry; + +FibEntry *fibEntry_Create(Name *name, strategy_type fwdStrategy); + +/** + * Decrements the reference count by one, and destroys the memory after last + * release + * + */ +void fibEntry_Release(FibEntry **fibEntryPtr); + +/** + * Returns a reference counted copy of the fib entry + * + * The reference count is increased by one. The returned value must be + * released via fibEnty_Release(). + * + * @param [in] fibEntry An allocated FibEntry + * + * @return non-null A reference counted copy of the fibEntry + * + */ +FibEntry *fibEntry_Acquire(const FibEntry *fibEntry); + +void fibEntry_SetStrategy(FibEntry *fibEntry, strategy_type strategy); + +void fibEntry_AddNexthop(FibEntry *fibEntry, unsigned connectionId); + +void fibEntry_RemoveNexthopByConnectionId(FibEntry *fibEntry, + unsigned connectionId); + +size_t fibEntry_NexthopCount(const FibEntry *fibEntry); + +/** + * @function fibEntry_GetNexthops + * @abstract Returns the nexthop set of the FIB entry. You must Acquire if it + * will be saved. + * @discussion + * Returns the next hop set for the FIB entry. + */ +const NumberSet *fibEntry_GetNexthops(const FibEntry *fibEntry); + +const NumberSet *fibEntry_GetNexthopsFromForwardingStrategy( + const FibEntry *fibEntry, const Message *interestMessage); + +void fibEntry_ReceiveObjectMessage(const FibEntry *fibEntry, + const NumberSet *egressId, + const Message *objectMessage, Ticks rtt); + +void fibEntry_OnTimeout(const FibEntry *fibEntry, const NumberSet *egressId); + +strategy_type fibEntry_GetFwdStrategyType(const FibEntry *fibEntry); + +StrategyImpl *fibEntry_GetFwdStrategy(const FibEntry *fibEntry); + +/** + * @function fibEntry_GetPrefix + * @abstract Returns a copy of the prefix. + * @return A reference counted copy that you must destroy + */ +Name *fibEntry_GetPrefix(const FibEntry *fibEntry); + +#ifdef WITH_MAPME + +/** + * @function fibEntry_AddNexthopByConnectionId + * @abstract Adds a next hop directly from the connection id. + * @param [in] fibEntry - Pointer to the FIB entry. + * @return The sequence number stored in the FIB entry. + */ +void fibEntry_AddNexthopByConnectionId(FibEntry *fibEntry, + unsigned connectionId); + +/** + * @function fibEntry_getUserData + * @abstract Returns user data associated to the FIB entry. + * @param [in] fibEntry - Pointer to the FIB entry. + * @return User data as a void pointer + */ +void *fibEntry_getUserData(const FibEntry *fibEntry); + +/** + * @function fibEntry_getUserData + * @abstract Associates user data and release callback to a FIB entry. + * @param [in] fibEntry - Pointer to the FIB entry. + * @param [in] userData - Generic pointer to user data + * @param [in@ userDataRelease - Callback used to release user data upon change + * of FIB entry removal. + */ +void fibEntry_setUserData(FibEntry *fibEntry, const void *userData, + void (*userDataRelease)(void **)); + +#endif /* WITH_MAPME */ + +#endif // fibEntry_h diff --git a/hicn-light/src/processor/fibEntryList.c b/hicn-light/src/processor/fibEntryList.c new file mode 100755 index 000000000..2221fa614 --- /dev/null +++ b/hicn-light/src/processor/fibEntryList.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include +#include + +struct fib_entry_list { + PARCArrayList *listOfFibEntries; +}; + +static void fibEntryList_ListDestroyer(void **voidPtr) { + FibEntry **entryPtr = (FibEntry **)voidPtr; + fibEntry_Release(entryPtr); +} + +FibEntryList *fibEntryList_Create() { + FibEntryList *fibEntryList = + parcMemory_AllocateAndClear(sizeof(FibEntryList)); + parcAssertNotNull(fibEntryList, + "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(FibEntryList)); + fibEntryList->listOfFibEntries = + parcArrayList_Create(fibEntryList_ListDestroyer); + return fibEntryList; +} + +void fibEntryList_Destroy(FibEntryList **listPtr) { + parcAssertNotNull(listPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*listPtr, "Parameter must dereference to non-null pointer"); + + FibEntryList *list = *listPtr; + parcArrayList_Destroy(&list->listOfFibEntries); + parcMemory_Deallocate((void **)&list); + listPtr = NULL; +} + +void fibEntryList_Append(FibEntryList *list, FibEntry *fibEntry) { + parcAssertNotNull(list, "Parameter list must be non-null pointer"); + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null pointer"); + + FibEntry *copy = fibEntry_Acquire(fibEntry); + parcArrayList_Add(list->listOfFibEntries, copy); +} + +size_t fibEntryList_Length(const FibEntryList *list) { + parcAssertNotNull(list, "Parameter list must be non-null pointer"); + return parcArrayList_Size(list->listOfFibEntries); +} + +const FibEntry *fibEntryList_Get(const FibEntryList *list, size_t index) { + parcAssertNotNull(list, "Parameter list must be non-null pointer"); + FibEntry *entry = parcArrayList_Get(list->listOfFibEntries, index); + return entry; +} diff --git a/hicn-light/src/processor/fibEntryList.h b/hicn-light/src/processor/fibEntryList.h new file mode 100755 index 000000000..0f6066435 --- /dev/null +++ b/hicn-light/src/processor/fibEntryList.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file fibEntryList.h + * @brief A typesafe list of FibEntry + * + * <#Detailed Description#> + * + */ + +#ifndef fibEntryList_h +#define fibEntryList_h + +#include + +struct fib_entry_list; +typedef struct fib_entry_list FibEntryList; + +/** + * Creates an emtpy FIB entry list + * + * Must be destroyed with fibEntryList_Destroy. + * + * @retval non-null An allocated FibEntryList + * @retval null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +FibEntryList *fibEntryList_Create(void); + +/** + * @function FibEntryList_Detroy + * @abstract Destroys the list and all entries. + * @discussion + * <#Discussion#> + * + * @param <#param1#> + */ +void fibEntryList_Destroy(FibEntryList **listPtr); + +/** + * @function fibEntryList_Append + * @abstract Will store a reference counted copy of the entry. + * @discussion + * Will create and store a reference counted copy. You keep ownership + * of the parameter fibEntry. + * + * @param <#param1#> + * @return <#return#> + */ +void fibEntryList_Append(FibEntryList *list, FibEntry *fibEntry); + +/** + * Returns the number of entries in the list + * + * <#Paragraphs Of Explanation#> + * + * @param [in] list An allocated FibEntryList + * + * @retval number The number of entries in the list + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t fibEntryList_Length(const FibEntryList *list); + +/** + * @function fibEntryList_Get + * @abstract Gets an element. This is the internal reference, do not destroy. + * @discussion + * Returns an internal reference from the list. You must not destroy it. + * Will assert if you go off the end of the list. + * + * @param <#param1#> + * @return <#return#> + */ +const FibEntry *fibEntryList_Get(const FibEntryList *list, size_t index); +#endif // fibEntryList_h diff --git a/hicn-light/src/processor/hashTableFunction.c b/hicn-light/src/processor/hashTableFunction.c new file mode 100755 index 000000000..6e70ef91a --- /dev/null +++ b/hicn-light/src/processor/hashTableFunction.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +#include +#include + +#include + +// ====================================================================== +// Hash table key functions +// We use a Message as the key data type + +bool hashTableFunction_MessageNameEquals(const void *messageA, + const void *messageB) { + const Message *a = (const Message *)messageA; + const Message *b = (const Message *)messageB; + + return name_Equals(message_GetName(a), message_GetName(b)); +} + +HashCodeType hashTableFunction_MessageNameHashCode(const void *messageA) { + const Message *message = (const Message *)messageA; + Name *name = message_GetName(message); + + // we want the cumulative hash for the whole name + uint32_t hash = name_HashCode(name); + + return hash; +} \ No newline at end of file diff --git a/hicn-light/src/processor/hashTableFunction.h b/hicn-light/src/processor/hashTableFunction.h new file mode 100755 index 000000000..eb9989086 --- /dev/null +++ b/hicn-light/src/processor/hashTableFunction.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file hashTableFunction.h + * @brief These functions are used in PARCHashCodeTables by the + * MatchingRulesTable and ContentStore and PIT. They perform the equality + * and has generation needed by the PARCHashCodeTable. + * + */ +#ifndef hashTableFunction_h +#define hashTableFunction_h + +#include + +// ========================================================== +// These functions operate on a message as the key in the HashTable. +// The functions use void * rather than message instances in the function +// signature because it is using generic has code tables from PARC Library + +/** + * Determine if the Names of two `message` instances are equal. + * + * The following equivalence relations on non-null `message` instances are + * maintained: + * + * * It is reflexive: for any non-null reference value x, + * `hashTableFunction_MessageNameEquals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `message_Equals(x, y)` must return true if and only if + * `hashTableFunction_MessageNameEquals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `hashTableFunction_MessageNameEquals(x, y)` returns true and + * `hashTableFunction_MessageNameEquals(y, z)` returns true, + * then `hashTableFunction_MessageNameEquals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `hashTableFunction_MessageNameEquals(x, y)` consistently + * return true or consistently return false. + * + * * For any non-null reference value x, + * `hashTableFunction_MessageNameEquals(x, NULL)` must return false. + * + * @param a A pointer to a `message` instance. + * @param b A pointer to a `message` instance. + * @return true if the names of the two `message` instances are equal. + */ +bool hashTableFunction_MessageNameEquals(const void *messageA, + const void *messageB); + +/** + * @function hashTableFunction_NameHashCode + * @abstract Computes the hash of the entire name in a message + * + * @param messageA is a message + * @return A non-cryptographic hash of Name + */ +HashCodeType hashTableFunction_MessageNameHashCode(const void *messageA); +#endif // hashTableFunction_h \ No newline at end of file diff --git a/hicn-light/src/processor/matchingRulesTable.c b/hicn-light/src/processor/matchingRulesTable.c new file mode 100755 index 000000000..56e59c29e --- /dev/null +++ b/hicn-light/src/processor/matchingRulesTable.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +#include +#include +#include + +struct matching_rules_table { + // using this wrapper we can manatain multiple hash tables indexed in + // different ways + // for now we use only a table indexed by name + + PARCHashCodeTable *tableByName; + PARCHashCodeTable_Destroyer dataDestroyer; +}; + +static PARCHashCodeTable *matchingRulesTable_GetTableForMessage( + const MatchingRulesTable *pit, const Message *interestMessage); + +// ====================================================================== + +MatchingRulesTable *matchingRulesTable_Create( + PARCHashCodeTable_Destroyer dataDestroyer) { + size_t initialSize = 65535; + + MatchingRulesTable *table = + parcMemory_AllocateAndClear(sizeof(MatchingRulesTable)); + parcAssertNotNull(table, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(MatchingRulesTable)); + table->dataDestroyer = dataDestroyer; + + table->tableByName = parcHashCodeTable_Create_Size( + hashTableFunction_MessageNameEquals, + hashTableFunction_MessageNameHashCode, NULL, dataDestroyer, initialSize); + + return table; +} + +void matchingRulesTable_Destroy(MatchingRulesTable **tablePtr) { + parcAssertNotNull(tablePtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*tablePtr, + "Parameter must dereference to non-null pointer"); + + MatchingRulesTable *table = *tablePtr; + + parcHashCodeTable_Destroy(&table->tableByName); + + parcMemory_Deallocate((void **)&table); + *tablePtr = NULL; +} + +void *matchingRulesTable_Get(const MatchingRulesTable *rulesTable, + const Message *message) { + parcAssertNotNull(rulesTable, "Parameter rulesTable must be non-null"); + parcAssertNotNull(message, "Parameter message must be non-null"); + + PARCHashCodeTable *hashTable = + matchingRulesTable_GetTableForMessage(rulesTable, message); + return parcHashCodeTable_Get(hashTable, message); +} + +PARCArrayList *matchingRulesTable_GetUnion(const MatchingRulesTable *table, + const Message *message) { + PARCArrayList *list = parcArrayList_Create_Capacity(NULL, NULL, 3); + + void *dataByName = parcHashCodeTable_Get(table->tableByName, message); + if (dataByName) { + parcArrayList_Add(list, dataByName); + } + + return list; +} + +void matchingRulesTable_RemoveFromBest(MatchingRulesTable *rulesTable, + const Message *message) { + parcAssertNotNull(rulesTable, "Parameter rulesTable must be non-null"); + parcAssertNotNull(message, "Parameter message must be non-null"); + + PARCHashCodeTable *hashTable = + matchingRulesTable_GetTableForMessage(rulesTable, message); + parcHashCodeTable_Del(hashTable, message); +} + +void matchingRulesTable_RemoveFromAll(MatchingRulesTable *rulesTable, + const Message *message) { + parcAssertNotNull(rulesTable, "Parameter rulesTable must be non-null"); + parcAssertNotNull(message, "Parameter message must be non-null"); + + parcHashCodeTable_Del(rulesTable->tableByName, message); +} + +bool matchingRulesTable_AddToBestTable(MatchingRulesTable *rulesTable, + Message *key, void *data) { + parcAssertNotNull(rulesTable, "Parameter rulesTable must be non-null"); + parcAssertNotNull(key, "Parameter key must be non-null"); + parcAssertNotNull(data, "Parameter data must be non-null"); + + PARCHashCodeTable *hashTable = + matchingRulesTable_GetTableForMessage(rulesTable, key); + + bool success = parcHashCodeTable_Add(hashTable, key, data); + + return success; +} + +// ======================================================================================== + +static PARCHashCodeTable *matchingRulesTable_GetTableForMessage( + const MatchingRulesTable *pit, const Message *interestMessage) { + PARCHashCodeTable *table; + table = pit->tableByName; + + return table; +} diff --git a/hicn-light/src/processor/matchingRulesTable.h b/hicn-light/src/processor/matchingRulesTable.h new file mode 100755 index 000000000..96d099430 --- /dev/null +++ b/hicn-light/src/processor/matchingRulesTable.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @header matchingRulesTable + * @abstract A generic table (void *) that matches a Message + * @discussion + * Matching is done based on Name + * + * When used in the PIT, one calls + * matchingRulesTable_AddToBestTable() to add an interest to the + * "best" (i.e. most restrictive match) table, then calls + * matchingRulesTable_GetUnion() on a content object to match + * against all of them. + * + * When used in a ContentStore, one calls + * matchingRulesTable_AddToAllTables() to index a Content Object in + * all the tables. one then calls matchingRulesTable_Get() with an + * Interest to do the "best" matching (i.e by hash first, then keyid, then just + * by name). + * + */ + +#ifndef matchingRulesTable_h +#define matchingRulesTable_h + +#include +#include +#include + +struct matching_rules_table; +typedef struct matching_rules_table MatchingRulesTable; + +/** + * Creates a MatchigRulesTable and specifies the function to call to de-allocate + * an entry + * + * The datadestroyer will be called when an entry is removed from a table. It + * may be NULL. + */ +MatchingRulesTable *matchingRulesTable_Create( + PARCHashCodeTable_Destroyer dataDestroyer); + +/** + * Destroys the table and removes all stored elements. + * + */ +void matchingRulesTable_Destroy(MatchingRulesTable **tablePtr); + +/** + * @function matchingRulesTable_Get + * @abstract Returns the data item that best matches the message. + * @discussion + * Indexed by NameAndContentObjectHash, NameAndKeyId, and Name, in that order. + * + * @return NULL if nothing matches, otherwise the stored value + */ +void *matchingRulesTable_Get(const MatchingRulesTable *table, + const Message *message); + +/** + * @function matchingRulesTable_GetUnion + * @abstract Returns matching data items from all index tables. + * @discussion + * The PARCArrayList does not have an item destructor, so destroying it will + * not affect the underlying data. + * + * @return Will not be NULL, but may be empty + */ +PARCArrayList *matchingRulesTable_GetUnion(const MatchingRulesTable *table, + const Message *message); + +/** + * @function matchingRulesTable_Add + * @abstract Adds the data to the best table + * @discussion + * The key must be derived from the data and destroyed when the data is + * destroyed. Only the data destroyer is called. + * + * No duplicates are allowed, will return false if not added. + * + * @return true if unique key and added, false if duplicate and no action taken. + */ +bool matchingRulesTable_AddToBestTable(MatchingRulesTable *rulesTable, + Message *key, void *data); + +/** + * @function matchingRulesTable_Remove + * @abstract Removes the matching entry from the best match table, calling the + * destroyer on the data. + */ +void matchingRulesTable_RemoveFromBest(MatchingRulesTable *rulesTable, + const Message *message); + +/** + * @function matchingRulesTable_RemoveFromAll + * @abstract Removes the message from all tables + */ +void matchingRulesTable_RemoveFromAll(MatchingRulesTable *rulesTable, + const Message *message); +#endif // matchingRulesTable_h diff --git a/hicn-light/src/processor/messageProcessor.c b/hicn-light/src/processor/messageProcessor.c new file mode 100755 index 000000000..8c03ee739 --- /dev/null +++ b/hicn-light/src/processor/messageProcessor.c @@ -0,0 +1,742 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +typedef struct processor_stats { + uint32_t countReceived; + uint32_t countInterestsReceived; + uint32_t countObjectsReceived; + + uint32_t countInterestsAggregated; + + uint32_t countDropped; + uint32_t countInterestsDropped; + uint32_t countDroppedNoRoute; + uint32_t countDroppedNoReversePath; + + uint32_t countDroppedConnectionNotFound; + uint32_t countObjectsDropped; + + uint32_t countSendFailures; + uint32_t countInterestForwarded; + uint32_t countObjectsForwarded; + uint32_t countInterestsSatisfiedFromStore; + + uint32_t countDroppedNoHopLimit; + uint32_t countDroppedZeroHopLimitFromRemote; + uint32_t countDroppedZeroHopLimitToRemote; +} _ProcessorStats; + +struct message_processor { + Forwarder *forwarder; + Logger *logger; + + PIT *pit; + ContentStoreInterface *contentStore; + FIB *fib; + + bool store_in_cache; + bool serve_from_cache; + + _ProcessorStats stats; +}; + +static void messageProcessor_Drop(MessageProcessor *processor, + Message *message); +static void messageProcessor_ReceiveInterest(MessageProcessor *processor, + Message *interestMessage); +static void messageProcessor_ReceiveContentObject(MessageProcessor *processor, + Message *objectMessage); +static unsigned messageProcessor_ForwardToNexthops(MessageProcessor *processor, + Message *message, + const NumberSet *nexthops); + +static void messageProcessor_ForwardToInterfaceId(MessageProcessor *processor, + Message *message, + unsigned interfaceId); + +// ============================================================ +// Public API + +MessageProcessor *messageProcessor_Create(Forwarder *forwarder) { + size_t objectStoreSize = + configuration_GetObjectStoreSize(forwarder_GetConfiguration(forwarder)); + + MessageProcessor *processor = + parcMemory_AllocateAndClear(sizeof(MessageProcessor)); + parcAssertNotNull(processor, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(MessageProcessor)); + memset(processor, 0, sizeof(MessageProcessor)); + + processor->forwarder = forwarder; + processor->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + processor->pit = pitStandard_Create(forwarder); + + processor->fib = fib_Create(); + + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(processor->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, "MessageProcessor %p created", (void *)processor); + } + + ContentStoreConfig contentStoreConfig = { + .objectCapacity = objectStoreSize, + }; + + processor->contentStore = + contentStoreLRU_Create(&contentStoreConfig, processor->logger); + + // the two flags for the cache are set to true by default. If the cache + // is active it always work as expected unless the use modifies this + // values using controller + processor->store_in_cache = true; + processor->serve_from_cache = true; + + return processor; +} + +void messageProcessor_SetContentObjectStoreSize( + MessageProcessor *processor, size_t maximumContentStoreSize) { + parcAssertNotNull(processor, "Parameter processor must be non-null"); + contentStoreInterface_Release(&processor->contentStore); + + ContentStoreConfig contentStoreConfig = {.objectCapacity = + maximumContentStoreSize}; + + processor->contentStore = + contentStoreLRU_Create(&contentStoreConfig, processor->logger); +} + +void messageProcessor_ClearCache(MessageProcessor *processor) { + parcAssertNotNull(processor, "Parameter processor must be non-null"); + size_t objectStoreSize = configuration_GetObjectStoreSize( + forwarder_GetConfiguration(processor->forwarder)); + + contentStoreInterface_Release(&processor->contentStore); + + ContentStoreConfig contentStoreConfig = { + .objectCapacity = objectStoreSize, + }; + + processor->contentStore = + contentStoreLRU_Create(&contentStoreConfig, processor->logger); +} + +ContentStoreInterface *messageProcessor_GetContentObjectStore( + const MessageProcessor *processor) { + parcAssertNotNull(processor, "Parameter processor must be non-null"); + return processor->contentStore; +} + +void messageProcessor_Destroy(MessageProcessor **processorPtr) { + parcAssertNotNull(processorPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*processorPtr, "Parameter dereference to non-null pointer"); + + MessageProcessor *processor = *processorPtr; + + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(processor->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, "MessageProcessor %p destroyed", (void *)processor); + } + + logger_Release(&processor->logger); + fib_Destroy(&processor->fib); + contentStoreInterface_Release(&processor->contentStore); + pit_Release(&processor->pit); + + parcMemory_Deallocate((void **)&processor); + *processorPtr = NULL; +} + +void messageProcessor_Receive(MessageProcessor *processor, Message *message) { + parcAssertNotNull(processor, "Parameter processor must be non-null"); + parcAssertNotNull(message, "Parameter message must be non-null"); + + processor->stats.countReceived++; + + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + char *nameString = name_ToString(message_GetName(message)); + logger_Log(processor->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, "Message %p ingress %3u length %5u received name %s", + (void *)message, message_GetIngressConnectionId(message), + message_Length(message), nameString); + parcMemory_Deallocate((void **)&nameString); + } + + switch (message_GetType(message)) { + case MessagePacketType_Interest: + messageProcessor_ReceiveInterest(processor, message); + break; + + case MessagePacketType_ContentObject: + messageProcessor_ReceiveContentObject(processor, message); + break; + + default: + messageProcessor_Drop(processor, message); + break; + } + + // if someone wanted to save it, they made a copy + message_Release(&message); +} + +bool messageProcessor_AddOrUpdateRoute(MessageProcessor *processor, + add_route_command *control, + unsigned ifidx) { + Configuration *config = forwarder_GetConfiguration(processor->forwarder); + + const char *prefixStr = utils_PrefixLenToString( + control->addressType, &control->address, &control->len); + strategy_type fwdStrategy = + configuration_GetForwardingStrategy(config, prefixStr); + if (fwdStrategy == LAST_STRATEGY_VALUE) { + fwdStrategy = SET_STRATEGY_LOADBALANCER; + } + + Name *prefix = name_CreateFromAddress(control->addressType, control->address, + control->len); + FibEntry *entry = fib_Contains(processor->fib, prefix); + bool newEntry = false; + if (entry != NULL) { + fibEntry_AddNexthop(entry, ifidx); + } else { + newEntry = true; + entry = fibEntry_Create(prefix, fwdStrategy); + fibEntry_AddNexthop(entry, ifidx); + fib_Add(processor->fib, entry); + } + + name_Release(&prefix); + if (newEntry && (fwdStrategy == SET_STRATEGY_LOADBALANCER_WITH_DELAY)) { + strategyLoadBalancerWithPD_SetConnectionTable( + fibEntry_GetFwdStrategy(entry), + forwarder_GetConnectionTable(processor->forwarder)); + } + + return true; +} + +bool messageProcessor_RemoveRoute(MessageProcessor *processor, + remove_route_command *control, + unsigned ifidx) { + Name *name = name_CreateFromAddress(control->addressType, control->address, + control->len); + fib_Remove(processor->fib, name, ifidx); + name_Release(&name); + + return true; +} + +void messageProcessor_RemoveConnectionIdFromRoutes(MessageProcessor *processor, + unsigned connectionId) { + fib_RemoveConnectionId(processor->fib, connectionId); +} + +void processor_SetStrategy(MessageProcessor *processor, Name *prefix, + strategy_type strategy) { + FibEntry *entry = fib_Contains(processor->fib, prefix); + if (entry != NULL) { + fibEntry_SetStrategy(entry, strategy); + if (strategy == SET_STRATEGY_LOADBALANCER_WITH_DELAY) { + strategyLoadBalancerWithPD_SetConnectionTable( + fibEntry_GetFwdStrategy(entry), + forwarder_GetConnectionTable(processor->forwarder)); + } + } +} + +FibEntryList *messageProcessor_GetFibEntries(MessageProcessor *processor) { + parcAssertNotNull(processor, "Parameter processor must be non-null"); + return fib_GetEntries(processor->fib); +} + +// ============================================================ +// Internal API + +/** + * @function messageProcessor_Drop + * @abstract Whenever we "drop" a message, increment countes + * @discussion + * This is a bookkeeping function. It increments the appropriate counters. + * + * The default action for a message is to destroy it in + * messageProcessor_Receive(), so this function does not need to do + * that. + * + */ +static void messageProcessor_Drop(MessageProcessor *processor, + Message *message) { + processor->stats.countDropped++; + + switch (message_GetType(message)) { + case MessagePacketType_Interest: + processor->stats.countInterestsDropped++; + break; + + case MessagePacketType_ContentObject: + processor->stats.countObjectsDropped++; + break; + + default: + break; + } + + // dont destroy message here, its done at end of receive +} + +/** + * @function messageProcessor_AggregateInterestInPit + * @abstract Try to aggregate the interest in the PIT + * @discussion + * Tries to aggregate the interest with another interest. + * + * @return true if interest aggregagted (no more forwarding needed), false if + * need to keep processing it. + */ +static bool messageProcessor_AggregateInterestInPit(MessageProcessor *processor, + Message *interestMessage) { + PITVerdict verdict = pit_ReceiveInterest(processor->pit, interestMessage); + + if (verdict == PITVerdict_Aggregate) { + // PIT has it, we're done + processor->stats.countInterestsAggregated++; + + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log( + processor->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, "Message %p aggregated in PIT (aggregated count %u)", + (void *)interestMessage, processor->stats.countInterestsAggregated); + } + + return true; + } + + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log( + processor->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, "Message %p not aggregated in PIT (aggregated count %u)", + (void *)interestMessage, processor->stats.countInterestsAggregated); + } + + return false; +} + +static bool _satisfyFromContentStore(MessageProcessor *processor, + Message *interestMessage) { + bool result = false; + + if (message_GetInterestLifetimeTicks(interestMessage) == 0) { + return false; + } + + if (!processor->serve_from_cache) { + return result; + } + + // See if there's a match in the store. + Message *objectMessage = contentStoreInterface_MatchInterest( + processor->contentStore, interestMessage, + forwarder_GetTicks(processor->forwarder)); + + if (objectMessage != NULL) { + // Remove it from the PIT. nexthops is allocated, so need to destroy + NumberSet *nexthops = pit_SatisfyInterest(processor->pit, objectMessage); + parcAssertNotNull( + nexthops, + "Illegal state: got a null nexthops for an interest we just inserted."); + + // send message in reply, then done + processor->stats.countInterestsSatisfiedFromStore++; + + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug, __func__, + "Message %p satisfied from content store (satisfied count %u)", + (void *)interestMessage, + processor->stats.countInterestsSatisfiedFromStore); + } + + message_ResetPathLabel(objectMessage); + + messageProcessor_ForwardToNexthops(processor, objectMessage, nexthops); + numberSet_Release(&nexthops); + + result = true; + } + + return result; +} + +/** + * @function messageProcessor_ForwardViaFib + * @abstract Try to forward the interest via the FIB + * @discussion + * This calls messageProcessor_ForwardToNexthops(), so if we find + * any nexthops, the interest will be sent on its way. Depending on the + * IoOperations of each nexthop, it may be a deferred write and bump up the + * interestMessage refernce count, or it may copy the data out. + * + * A TRUE return means we did our best to forward it via the routes. If those + * routes are actually down or have errors, we still return TRUE. A FALSE + * return means there were no routes to try. + * + * @return true if we found a route and tried to forward it, false if no route + */ +static bool messageProcessor_ForwardViaFib(MessageProcessor *processor, + Message *interestMessage) { + FibEntry *fibEntry = fib_Match(processor->fib, interestMessage); + if (fibEntry == NULL) { + return false; + } + + PitEntry *pitEntry = pit_GetPitEntry(processor->pit, interestMessage); + if (pitEntry == NULL) { + return false; + } + + pitEntry_AddFibEntry(pitEntry, fibEntry); + + NumberSet *nexthops = (NumberSet *)fibEntry_GetNexthopsFromForwardingStrategy( + fibEntry, interestMessage); + // this requires some additional checks. It may happen that some of the output + // faces selected by the forwarding strategy are not usable. So far all the + // forwarding strategy return only valid faces (or an empty list) + for (unsigned i = 0; i < numberSet_Length(nexthops); i++) { + pitEntry_AddEgressId(pitEntry, numberSet_GetItem(nexthops, i)); + } + + // The function GetPitEntry encreases the ref counter in the pit entry + // we need to decrease it + pitEntry_Release(&pitEntry); + + if (messageProcessor_ForwardToNexthops(processor, interestMessage, nexthops) > + 0) { + numberSet_Release(&nexthops); + return true; + } else { + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug, __func__, + "Message %p returned an emtpy next hop set", + (void *)interestMessage); + } + } + + return false; +} + +/** + * @function messageProcessor_ReceiveInterest + * @abstract Receive an interest from the network + * @discussion + * (1) if interest in the PIT, aggregate in PIT + * (2) if interest in the ContentStore, reply + * (3) if in the FIB, forward + * (4) drop + * + */ +static void messageProcessor_ReceiveInterest(MessageProcessor *processor, + Message *interestMessage) { + processor->stats.countInterestsReceived++; + + // (1) Try to aggregate in PIT + if (messageProcessor_AggregateInterestInPit(processor, interestMessage)) { + // done + return; + } + + // At this point, we just created a PIT entry. If we don't forward the + // interest, we need to remove the PIT entry. + + // (2) Try to satisfy from content store + if (_satisfyFromContentStore(processor, interestMessage)) { + // done + // If we found a content object in the CS, + // messageProcess_SatisfyFromContentStore already cleared the PIT state + return; + } + + // (3) Try to forward it + if (messageProcessor_ForwardViaFib(processor, interestMessage)) { + // done + return; + } + + // Remove the PIT entry? + processor->stats.countDroppedNoRoute++; + + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(processor->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, "Message %p did not match FIB, no route (count %u)", + (void *)interestMessage, processor->stats.countDroppedNoRoute); + } + + messageProcessor_Drop(processor, interestMessage); +} + +/** + * @function messageProcessor_ReceiveContentObject + * @abstract Process an in-bound content object + * @discussion + * (1) If it does not match anything in the PIT, drop it + * (2) Add to Content Store + * (3) Reverse path forward via PIT entries + * + * @param <#param1#> + */ +static void messageProcessor_ReceiveContentObject(MessageProcessor *processor, + Message *message) { + processor->stats.countObjectsReceived++; + + NumberSet *ingressSetUnion = pit_SatisfyInterest(processor->pit, message); + + if (numberSet_Length(ingressSetUnion) == 0) { + // (1) If it does not match anything in the PIT, drop it + processor->stats.countDroppedNoReversePath++; + + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug, __func__, + "Message %p did not match PIT, no reverse path (count %u)", + (void *)message, processor->stats.countDroppedNoReversePath); + } + + // we store the packets in the content store enven in the case where there + // is no match in the PIT table in this way the applications can push the + // content in the CS of the forwarder. We allow this only for local faces + bool isLocal = connection_IsLocal(connectionTable_FindById( + forwarder_GetConnectionTable(processor->forwarder), + message_GetIngressConnectionId((const Message *)message))); + if (processor->store_in_cache && isLocal) { + uint64_t currentTimeTicks = forwarder_GetTicks(processor->forwarder); + contentStoreInterface_PutContent(processor->contentStore, message, + currentTimeTicks); + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug, __func__, + "Message %p sotred in the CS anyway", (void *)message); + } + } + + messageProcessor_Drop(processor, message); + } else { + // (2) Add to Content Store. Store may remove expired content, if necessary, + // depending on store policy. + if (processor->store_in_cache) { + uint64_t currentTimeTicks = forwarder_GetTicks(processor->forwarder); + contentStoreInterface_PutContent(processor->contentStore, message, + currentTimeTicks); + } + // (3) Reverse path forward via PIT entries + messageProcessor_ForwardToNexthops(processor, message, ingressSetUnion); + } + + numberSet_Release(&ingressSetUnion); +} + +/** + * @function messageProcessor_ForwardToNexthops + * @abstract Try to forward to each nexthop listed in the NumberSet + * @discussion + * Will not forward to the ingress connection. + * + * @return The number of nexthops tried + */ +static unsigned messageProcessor_ForwardToNexthops(MessageProcessor *processor, + Message *message, + const NumberSet *nexthops) { + unsigned forwardedCopies = 0; + + size_t length = numberSet_Length(nexthops); + + unsigned ingressId = message_GetIngressConnectionId(message); + uint32_t old_path_label = 0; + + if (message_GetType(message) == MessagePacketType_ContentObject) { + old_path_label = message_GetPathLabel(message); + } + + for (size_t i = 0; i < length; i++) { + unsigned egressId = numberSet_GetItem(nexthops, i); + if (egressId != ingressId) { + forwardedCopies++; + messageProcessor_ForwardToInterfaceId(processor, message, egressId); + + if (message_GetType(message) == MessagePacketType_ContentObject) { + // everytime we send out a message we need to restore the original path + // label of the message this is important because we keep a single copy + // of the message (single pointer) and we modify the path label at each + // send. + message_SetPathLabel(message, old_path_label); + } + } + } + return forwardedCopies; +} + +/** + * caller has checked that the hop limit is ok. Try to send out the connection. + */ +static void messageProcessor_SendWithGoodHopLimit(MessageProcessor *processor, + Message *message, + unsigned interfaceId, + const Connection *conn) { + bool success = connection_Send(conn, message); + if (success) { + switch (message_GetType(message)) { + case MessagePacketType_Interest: + processor->stats.countInterestForwarded++; + break; + + case MessagePacketType_ContentObject: + processor->stats.countObjectsForwarded++; + break; + + default: + break; + } + + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log( + processor->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, "forward message %p to interface %u (int %u, obj %u)", + (void *)message, interfaceId, processor->stats.countInterestForwarded, + processor->stats.countObjectsForwarded); + } + } else { + processor->stats.countSendFailures++; + + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug, __func__, + "forward message %p to interface %u send failure (count %u)", + (void *)message, interfaceId, + processor->stats.countSendFailures); + } + messageProcessor_Drop(processor, message); + } +} + +/* + * If the hoplimit is equal to 0, then we may only forward it to local + * applications. Otherwise, we may forward it off the system. + * + */ +static void messageProcessor_ForwardToInterfaceId(MessageProcessor *processor, + Message *message, + unsigned interfaceId) { + ConnectionTable *connectionTable = + forwarder_GetConnectionTable(processor->forwarder); + const Connection *conn = + connectionTable_FindById(connectionTable, interfaceId); + + if (conn != NULL) { + messageProcessor_SendWithGoodHopLimit(processor, message, interfaceId, + conn); + } else { + processor->stats.countDroppedConnectionNotFound++; + + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug, __func__, + "forward message %p to interface %u not found (count %u)", + (void *)message, interfaceId, + processor->stats.countDroppedConnectionNotFound); + } + + messageProcessor_Drop(processor, message); + } +} + +void messageProcessor_SetCacheStoreFlag(MessageProcessor *processor, bool val) { + processor->store_in_cache = val; +} + +bool messageProcessor_GetCacheStoreFlag(MessageProcessor *processor) { + return processor->store_in_cache; +} + +void messageProcessor_SetCacheServeFlag(MessageProcessor *processor, bool val) { + processor->serve_from_cache = val; +} + +bool messageProcessor_GetCacheServeFlag(MessageProcessor *processor) { + return processor->serve_from_cache; +} + +#ifdef WITH_MAPME + +FIB *messageProcessor_getFib(MessageProcessor *processor) { + return processor->fib; +} + +#endif /* WITH_MAPME */ diff --git a/hicn-light/src/processor/messageProcessor.h b/hicn-light/src/processor/messageProcessor.h new file mode 100755 index 000000000..ce3049938 --- /dev/null +++ b/hicn-light/src/processor/messageProcessor.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file messageProcessor.h + * @brief Executes the set of rules dictated by the PacketType + * + * This is a "run-to-completion" handling of a message based on the PacketType. + * + * The MessageProcessor also owns the PIT and FIB tables. + * + */ + +#ifndef messageProcessor_h +#define messageProcessor_h + +#include +#include +#include + +#include + +struct message_processor; +typedef struct message_processor MessageProcessor; + +/** + * Allocates a MessageProcessor along with PIT, FIB and ContentStore tables + * + * The hicn-light pointer is primarily used for logging (forwarder_Log), getting + * the configuration, and accessing the connection table. + * + * @param [in] Pointer to owning hicn-light process + * + * @retval non-null An allocated message processor + * @retval null An error + * + */ +MessageProcessor *messageProcessor_Create(Forwarder *forwarder); + +/** + * Deallocates a message processor an all internal tables + * + * @param [in,out] processorPtr Pointer to message processor to de-allocate, + * will be NULL'd. + */ +void messageProcessor_Destroy(MessageProcessor **processorPtr); + +/** + * @function messageProcessor_Receive + * @abstract Process the message, takes ownership of the memory. + * @discussion + * Will call destroy on the memory when done with it, so if the caller wants + * to keep it, make a reference counted copy. + * + * Receive may modify some fields in the message, such as the HopLimit field. + */ +void messageProcessor_Receive(MessageProcessor *procesor, Message *message); + +/** + * Adds or updates a route in the FIB + * + * If the route already exists, it is replaced + * + * @param [in] procesor An allocated message processor + * @param [in] route The route to update + * + * @retval true added or updated + * @retval false An error + */ +bool messageProcessor_AddOrUpdateRoute(MessageProcessor *processor, + add_route_command *control, + unsigned ifidx); + +/** + * Removes a route from the FIB + * + * Removes a specific nexthop for a route. If there are no nexthops left after + * the removal, the entire route is deleted from the FIB. + * + * @param [in] procesor An allocated message processor + * @param [in] route The route to remove + * + * @retval true Route completely removed + * @retval false There is still a nexthop for the route + */ + +bool messageProcessor_RemoveRoute(MessageProcessor *processor, + remove_route_command *control, + unsigned ifidx); + +/** + * Removes a given connection id from all FIB entries + * + * Iterates the FIB and removes the given connection ID from every route. + */ +void messageProcessor_RemoveConnectionIdFromRoutes(MessageProcessor *processor, + unsigned connectionId); + +/** + * Returns a list of all FIB entries + * + * You must destroy the list. + * + * @retval non-null The list of FIB entries + * @retval null An error + */ +FibEntryList *messageProcessor_GetFibEntries(MessageProcessor *processor); + +/** + * Adjusts the ContentStore to the given size. + * + * This will destroy and re-create the content store, so any cached objects will + * be lost. + * + */ +void messageProcessor_SetContentObjectStoreSize(MessageProcessor *processor, + size_t maximumContentStoreSize); + +/** + * Return the interface to the currently instantiated ContentStore, if any. + * + * @param [in] processor the `MessageProcessor` from which to return the + * ContentStoreInterface. + * + */ +ContentStoreInterface *messageProcessor_GetContentObjectStore( + const MessageProcessor *processor); + +void messageProcessor_SetCacheStoreFlag(MessageProcessor *processor, bool val); + +bool messageProcessor_GetCacheStoreFlag(MessageProcessor *processor); + +void messageProcessor_SetCacheServeFlag(MessageProcessor *processor, bool val); + +bool messageProcessor_GetCacheServeFlag(MessageProcessor *processor); + +void messageProcessor_ClearCache(MessageProcessor *processor); + +void processor_SetStrategy(MessageProcessor *processor, Name *prefix, + strategy_type strategy); + +#ifdef WITH_MAPME + +/** + * @function messageProcessor_getFib + * @abstract Returns the hICN processor's FIB. + * @param [in] forwarder - Pointer to the hICN processor. + * @returns Pointer to the hICN FIB. + */ +FIB *messageProcessor_getFib(MessageProcessor *processor); + +#endif /* WITH_MAPME */ + +#endif // messageProcessor_h diff --git a/hicn-light/src/processor/pit.c b/hicn-light/src/processor/pit.c new file mode 100755 index 000000000..9cae4062e --- /dev/null +++ b/hicn-light/src/processor/pit.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Generic interface to PIT table + * + */ + +#include +#include +#include + +#include + +void *pit_Closure(const PIT *pit) { return pit->closure; } + +void pit_Release(PIT **pitPtr) { (*pitPtr)->release(pitPtr); } + +PITVerdict pit_ReceiveInterest(PIT *pit, Message *interestMessage) { + return pit->receiveInterest(pit, interestMessage); +} + +NumberSet *pit_SatisfyInterest(PIT *pit, const Message *objectMessage) { + return pit->satisfyInterest(pit, objectMessage); +} + +void pit_RemoveInterest(PIT *pit, const Message *interestMessage) { + pit->removeInterest(pit, interestMessage); +} + +PitEntry *pit_GetPitEntry(const PIT *pit, const Message *interestMessage) { + return pit->getPitEntry(pit, interestMessage); +} diff --git a/hicn-light/src/processor/pit.h b/hicn-light/src/processor/pit.h new file mode 100755 index 000000000..1f909be3e --- /dev/null +++ b/hicn-light/src/processor/pit.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file pit.h + * @brief The Pending Interest Table interface + * + * Interface for implementing a PIT table + * + */ + +#ifndef pit_h +#define pit_h + +#include +#include +#include +#include +#include + +struct pit; +typedef struct pit PIT; + +struct pit { + void (*release)(PIT **pitPtr); + PITVerdict (*receiveInterest)(PIT *pit, Message *interestMessage); + NumberSet *(*satisfyInterest)(PIT *pit, const Message *objectMessage); + void (*removeInterest)(PIT *pit, const Message *interestMessage); + PitEntry *(*getPitEntry)(const PIT *pit, const Message *interestMessage); + void *closure; +}; + +void *pit_Closure(const PIT *pit); + +/** + * Destroys the PIT table and all entries contained in it. + * + * PIT entries are reference counted, so if the user has stored one outside the + * PIT table it will still be valid. + * + * @param [in,out] pitPtr Double pointer to PIT table, will be NULLed + */ +void pit_Release(PIT **pitPtr); + +/** + * @function pit_ReceiveInterest + * @abstract Receives an interest and adds to PIT table + * @discussion + * If not present, adds entry to the PIT table and returns + * PIT_VERDICT_NEW_ENTRY. If present and aggregated, returns + * PIT_VERDICT_EXISTING_ENTRY. + * + * Some aggregated interests may return PIT_VERDICT_NEW_ENTRY if the interest + * needs to be forwarded again (e.g. the lifetime is extended). + * + * If the PIT stores the message in its table, it will store a reference + * counted copy. + * + * @return Verdict of receiving the interest + */ +PITVerdict pit_ReceiveInterest(PIT *pit, Message *interestMessage); + +/** + * @function pit_SatisfyInterest + * @abstract Tries to satisfy PIT entries based on the message, returning where + * to send message + * @discussion + * If matching interests are in the PIT, will return the set of reverse + * paths to use to forward the content object. + * + * The return value is allocated and must be destroyed. + * + * @return Set of ConnectionTable id's to forward the message, may be empty or + * NULL. Must be destroyed. + */ +NumberSet *pit_SatisfyInterest(PIT *pit, const Message *objectMessage); + +/** + * @function pit_RemoveInterest + * @abstract Unconditionally remove the interest from the PIT + * @discussion + * The PIT may store a specific name in several tables. This function will + * remove the interest from the specific table it lives it. It will not + * remove PIT entries in different tables with the same name. + * + * The different tables index interests based on their matching criteria, + * such as by name, by name and keyid, etc. + * + */ +void pit_RemoveInterest(PIT *pit, const Message *interestMessage); + +/** + * @function pit_GetPitEntry + * @abstract Retrieve the best matching PIT entry for the message. + * @discussion + * Returns a reference counted copy of the entry, must call + * pitEntry_Destory() on it. + * + * @return NULL if not in table, otherwise a reference counted copy of the entry + */ +PitEntry *pit_GetPitEntry(const PIT *pit, const Message *interestMessage); +#endif // pit_h diff --git a/hicn-light/src/processor/pitEntry.c b/hicn-light/src/processor/pitEntry.c new file mode 100755 index 000000000..38103cb8e --- /dev/null +++ b/hicn-light/src/processor/pitEntry.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include + +#include + +struct pit_entry { + Message *message; + NumberSet *ingressIdSet; + NumberSet *egressIdSet; + + FibEntry *fibEntry; + + Ticks creationTime; + Ticks expiryTime; + + unsigned refcount; +}; + +PitEntry *pitEntry_Create(Message *message, Ticks expiryTime, + Ticks creationTime) { + PitEntry *pitEntry = parcMemory_AllocateAndClear(sizeof(PitEntry)); + parcAssertNotNull(pitEntry, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(PitEntry)); + pitEntry->message = message; + pitEntry->ingressIdSet = numberSet_Create(); + pitEntry->egressIdSet = numberSet_Create(); + pitEntry->refcount = 1; + + // add the message to the reverse path set + numberSet_Add(pitEntry->ingressIdSet, + message_GetIngressConnectionId(message)); + + // hack in a 4-second timeout + pitEntry->expiryTime = expiryTime; + pitEntry->fibEntry = NULL; + + pitEntry->creationTime = creationTime; + return pitEntry; +} + +void pitEntry_Release(PitEntry **pitEntryPtr) { + parcAssertNotNull(pitEntryPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*pitEntryPtr, + "Parameter must dereference to non-null pointer"); + + PitEntry *pitEntry = *pitEntryPtr; + parcTrapIllegalValueIf(pitEntry->refcount == 0, + "Illegal state: has refcount of 0"); + + pitEntry->refcount--; + if (pitEntry->refcount == 0) { + if (pitEntry->fibEntry != NULL) { + fibEntry_Release(&pitEntry->fibEntry); + } + numberSet_Release(&pitEntry->ingressIdSet); + numberSet_Release(&pitEntry->egressIdSet); + message_Release(&pitEntry->message); + parcMemory_Deallocate((void **)&pitEntry); + } + *pitEntryPtr = NULL; +} + +PitEntry *pitEntry_Acquire(PitEntry *original) { + parcAssertNotNull(original, "Parameter original must be non-null"); + original->refcount++; + return original; +} + +void pitEntry_AddIngressId(PitEntry *pitEntry, unsigned ingressId) { + parcAssertNotNull(pitEntry, "Parameter pitEntry must be non-null"); + numberSet_Add(pitEntry->ingressIdSet, ingressId); +} + +void pitEntry_AddEgressId(PitEntry *pitEntry, unsigned egressId) { + parcAssertNotNull(pitEntry, "Parameter pitEntry must be non-null"); + numberSet_Add(pitEntry->egressIdSet, egressId); +} + +void pitEntry_AddFibEntry(PitEntry *pitEntry, FibEntry *fibEntry) { + parcAssertNotNull(pitEntry, "Parameter pitEntry must be non-null"); + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + // the fibEntry should be always the same for all the interests in the same + // pitEntry + if (pitEntry->fibEntry == NULL) { + fibEntry_Acquire(fibEntry); + pitEntry->fibEntry = fibEntry; + } +} + +FibEntry *pitEntry_GetFibEntry(PitEntry *pitEntry) { + parcAssertNotNull(pitEntry, "Parameter pitEntry must be non-null"); + return pitEntry->fibEntry; +} + +Ticks pitEntry_GetExpiryTime(const PitEntry *pitEntry) { + parcAssertNotNull(pitEntry, "Parameter pitEntry must be non-null"); + return pitEntry->expiryTime; +} + +Ticks pitEntry_GetCreationTime(const PitEntry *pitEntry) { + parcAssertNotNull(pitEntry, "Parameter pitEntry must be non-null"); + return pitEntry->creationTime; +} + +void pitEntry_SetExpiryTime(PitEntry *pitEntry, Ticks expiryTime) { + parcAssertNotNull(pitEntry, "Parameter pitEntry must be non-null"); + pitEntry->expiryTime = expiryTime; +} + +const NumberSet *pitEntry_GetIngressSet(const PitEntry *pitEntry) { + parcAssertNotNull(pitEntry, "Parameter pitEntry must be non-null"); + return pitEntry->ingressIdSet; +} + +const NumberSet *pitEntry_GetEgressSet(const PitEntry *pitEntry) { + parcAssertNotNull(pitEntry, "Parameter pitEntry must be non-null"); + return pitEntry->egressIdSet; +} + +Message *pitEntry_GetMessage(const PitEntry *pitEntry) { + parcAssertNotNull(pitEntry, "Parameter pitEntry must be non-null"); + return message_Acquire(pitEntry->message); +} diff --git a/hicn-light/src/processor/pitEntry.h b/hicn-light/src/processor/pitEntry.h new file mode 100755 index 000000000..b7d45e6a4 --- /dev/null +++ b/hicn-light/src/processor/pitEntry.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file pitEntry.h + * @brief The embodiment of a PIT entry + * + * Embodies a PIT entry + * + */ + +#ifndef pitEntry_h +#define pitEntry_h + +#include +#include +#include +#include + +struct pit_entry; +typedef struct pit_entry PitEntry; + +/** + * @function pitEntry_Create + * @abstract Takes ownership of the message inside the PitEntry + * @discussion + * When the PIT entry is destroyed, will call message_Release() + * on the message. + * + */ +PitEntry *pitEntry_Create(Message *message, Ticks expiryTime, + Ticks CreationTime); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] pitEntryPtr A pointer to a PitEntry instance pointer, which + * will be set to zero on return. + * + */ +void pitEntry_Release(PitEntry **pitEntryPtr); + +/** + * @function pitEntry_Acquire + * @abstract Returns a reference counted copy + * @discussion + * A reference counted copy that shares the same state as the original. + * Caller must use pitEntry_Release() on it when done. + * + * @return A reference counted copy, use Destroy on it. + */ +PitEntry *pitEntry_Acquire(PitEntry *original); + +/** + * @function pitEntry_AddIngressId + * @abstract Add an ingress connection id to the list of reverse paths + * @discussion + * A PitEntry has two NumberSets. The first is the set of ingress ports, + * which make up the reverse path. The second is the set of egress ports, which + * make up its forward path. + * + * This function tracks which reverse paths have sent us the interest. + * + * @param ingressId the reverse path + */ +void pitEntry_AddIngressId(PitEntry *pitEntry, unsigned ingressId); + +/** + * @function pitEntry_AddEgressId + * @abstract Add an egress connection id to the list of attempted paths + * @discussion + * A PitEntry has two NumberSets. The first is the set of ingress ports, + * which make up the reverse path. The second is the set of egress ports, which + * make up its forward path. + * + * This function tracks which forward paths we've tried for the interest. + * + * @param egressId the forwarded path + */ +void pitEntry_AddEgressId(PitEntry *pitEntry, unsigned egressId); + +void pitEntry_AddFibEntry(PitEntry *pitEntry, FibEntry *fibEntry); + +FibEntry *pitEntry_GetFibEntry(PitEntry *pitEntry); + +/** + * @function pitEntry_GetIngressSet + * @abstract The Ingress connection id set + * @discussion + * You must acquire a copy of the number set if you will store the result. + * This is the internal reference. + * + * @return May be empty, will not be null. Must be destroyed. + */ +const NumberSet *pitEntry_GetIngressSet(const PitEntry *pitEntry); + +/** + * @function pitEntry_GetEgressSet + * @abstract The Egress connection id set + * @discussion + * You must acquire a copy of the number set if you will store the result. + * This is the internal reference. + * + * @param <#param1#> + * @return May be empty, will not be null. Must be destroyed. + */ +const NumberSet *pitEntry_GetEgressSet(const PitEntry *pitEntry); + +/** + * @function pitEntry_GetMessage + * @abstract Gets the interest underpinning the PIT entry + * @discussion + * A reference counted copy, call Message_Release() on it. + * + * @return A reference counted copy, call Message_Release() on it. + */ +Message *pitEntry_GetMessage(const PitEntry *pitEntry); + +/** + * Returns the time (in ticks) at which the PIT entry is no longer valid + * + * The ExpiryTime is computed when the PIT entry is added (or via + * pitEntry_SetExpiryTime). It is the aboslute time (in Ticks) at which the Pit + * entry is no longer valid. + * + * @param [in] PitEntry An allocated PIT entry + * + * @retval number The abosolute time (in Ticks) of the Expiry + */ +Ticks pitEntry_GetExpiryTime(const PitEntry *pitEntry); + +Ticks pitEntry_GetCreationTime(const PitEntry *pitEntry); +/** + * Sets the ExpriyTime of the PIT entry to the given value + * + * It is probalby an error to set the expiryTime to a smaller value than + * currently set to, but this is not enforced. PIT entries use lazy delete. + * + * @param [in] pitEntry The allocated PIT entry to modify + * @param [in] expiryTime The new expiryTime (UTC in forwarder Ticks) + * + */ +void pitEntry_SetExpiryTime(PitEntry *pitEntry, Ticks expiryTime); + +#endif // pitEntry_h diff --git a/hicn-light/src/processor/pitStandard.c b/hicn-light/src/processor/pitStandard.c new file mode 100755 index 000000000..8d507626a --- /dev/null +++ b/hicn-light/src/processor/pitStandard.c @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * The pending interest table. + * + * Interest aggregation strategy: + * - The first Interest for a name is forwarded + * - A second Interest for a name from a different reverse path may be + * aggregated + * - A second Interest for a name from an existing Interest is forwarded + * - The Interest Lifetime is like a subscription time. A reverse path entry is + * removed once the lifetime is exceeded. + * - Whan an Interest arrives or is aggregated, the Lifetime for that reverse + * hop is extended. As a simplification, we only keep a single lifetime not per + * reverse hop. + * + */ + +#include +#include + +#define __STDC_FORMAT_MACROS +#include + +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +struct standard_pit; +typedef struct standard_pit StandardPIT; + +struct standard_pit { + Forwarder *forwarder; + Logger *logger; + PARCHashCodeTable *table; // PIT indexed by name +}; + +static void _pit_StoreInTable(StandardPIT *pit, Message *interestMessage); + +static void _pit_PitEntryDestroyer(void **dataPtr) { + pitEntry_Release((PitEntry **)dataPtr); +} + +static bool _pit_IngressSetContains(PitEntry *pitEntry, unsigned connectionId) { + const NumberSet *set = pitEntry_GetIngressSet(pitEntry); + bool numberInSet = numberSet_Contains(set, connectionId); + return numberInSet; +} + +static Ticks _pit_CalculateLifetime(StandardPIT *pit, + Message *interestMessage) { + uint64_t interestLifetimeTicks = + message_GetInterestLifetimeTicks(interestMessage); + if (interestLifetimeTicks == 0) { + interestLifetimeTicks = forwarder_NanosToTicks(4000000000ULL); + } + + Ticks expiryTime = forwarder_GetTicks(pit->forwarder) + interestLifetimeTicks; + return expiryTime; +} + +static void _pit_StoreInTable(StandardPIT *pit, Message *interestMessage) { + Message *key = message_Acquire(interestMessage); + + Ticks expiryTime = _pit_CalculateLifetime(pit, interestMessage); + + PitEntry *pitEntry = + pitEntry_Create(key, expiryTime, forwarder_GetTicks(pit->forwarder)); + + parcHashCodeTable_Add(pit->table, key, pitEntry); + + if (logger_IsLoggable(pit->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(pit->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, + "Message %p added to PIT (expiry %" PRIu64 ") ingress %u", + (void *)interestMessage, pitEntry_GetExpiryTime(pitEntry), + message_GetIngressConnectionId(interestMessage)); + } +} + +static void _pit_ExtendLifetime(StandardPIT *pit, PitEntry *pitEntry, + Message *interestMessage) { + Ticks expiryTime = _pit_CalculateLifetime(pit, interestMessage); + + if (expiryTime > pitEntry_GetExpiryTime(pitEntry)) + pitEntry_SetExpiryTime(pitEntry, expiryTime); +} + +// ====================================================================== +// Interface API + +static void _pitStandard_Destroy(PIT **pitPtr) { + parcAssertNotNull(pitPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*pitPtr, "Parameter must dereference to non-null pointer"); + + StandardPIT *pit = pit_Closure(*pitPtr); + + if (logger_IsLoggable(pit->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(pit->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, "PIT %p destroyed", (void *)pit); + } + + parcHashCodeTable_Destroy(&pit->table); + logger_Release(&pit->logger); + parcMemory_Deallocate(pitPtr); +} + +static PITVerdict _pitStandard_ReceiveInterest(PIT *generic, + Message *interestMessage) { + parcAssertNotNull(generic, "Parameter pit must be non-null"); + parcAssertNotNull(interestMessage, + "Parameter interestMessage must be non-null"); + + StandardPIT *pit = pit_Closure(generic); + + PitEntry *pitEntry = parcHashCodeTable_Get(pit->table, interestMessage); + + if (pitEntry) { + // has it expired? + Ticks now = forwarder_GetTicks(pit->forwarder); + if (now < pitEntry_GetExpiryTime(pitEntry)) { + _pit_ExtendLifetime(pit, pitEntry, interestMessage); + + // Is the reverse path already in the PIT entry? + if (_pit_IngressSetContains( + pitEntry, message_GetIngressConnectionId(interestMessage))) { + // It is already in the PIT entry, so this is a retransmission, so + // forward it. + + if (logger_IsLoggable(pit->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(pit->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, + "Message %p existing entry (expiry %" PRIu64 + ") and reverse path, forwarding", + (void *)interestMessage, pitEntry_GetExpiryTime(pitEntry)); + } + + return PITVerdict_Forward; + } + + // It is in the PIT but this is the first interest for the reverse path + pitEntry_AddIngressId(pitEntry, + message_GetIngressConnectionId(interestMessage)); + + if (logger_IsLoggable(pit->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(pit->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, + "Message %p existing entry (expiry %" PRIu64 + ") and reverse path is new, aggregate", + (void *)interestMessage, pitEntry_GetExpiryTime(pitEntry)); + } + + return PITVerdict_Aggregate; + } + // this is a timeout.... + FibEntry *fibEntry = pitEntry_GetFibEntry(pitEntry); + if (fibEntry != NULL) { + fibEntry_OnTimeout(fibEntry, pitEntry_GetEgressSet(pitEntry)); + } + + // it's an old entry, remove it + parcHashCodeTable_Del(pit->table, interestMessage); + } + + _pit_StoreInTable(pit, interestMessage); + + return PITVerdict_Forward; +} + +static NumberSet *_pitStandard_SatisfyInterest(PIT *generic, + const Message *objectMessage) { + parcAssertNotNull(generic, "Parameter pit must be non-null"); + parcAssertNotNull(objectMessage, "Parameter objectMessage must be non-null"); + + StandardPIT *pit = pit_Closure(generic); + + NumberSet *ingressSet = numberSet_Create(); + + PitEntry *pitEntry = parcHashCodeTable_Get(pit->table, objectMessage); + if (pitEntry) { + // here we need to check if the PIT entry is expired + // if so, remove the PIT entry. + Ticks now = forwarder_GetTicks(pit->forwarder); + if (now < pitEntry_GetExpiryTime(pitEntry)) { + // PIT entry is not expired, use it + FibEntry *fibEntry = pitEntry_GetFibEntry(pitEntry); + if (fibEntry != NULL) { + // this is a rough estimation of the residual RTT + Ticks rtt = forwarder_GetTicks(pit->forwarder) - + pitEntry_GetCreationTime(pitEntry); + fibEntry_ReceiveObjectMessage(fibEntry, pitEntry_GetEgressSet(pitEntry), + objectMessage, + rtt); // need to implement RTT + } + const NumberSet *is = pitEntry_GetIngressSet(pitEntry); + numberSet_AddSet(ingressSet, is); // with this we do a copy so we can + // remove the entry from the PIT + } + // remove the entry from the PIT. Key is a reference counted copy of the + // pit entry message + Message *key = pitEntry_GetMessage(pitEntry); + parcHashCodeTable_Del(pit->table, key); + message_Release(&key); + } + + return ingressSet; +} + +static void _pitStandard_RemoveInterest(PIT *generic, + const Message *interestMessage) { + parcAssertNotNull(generic, "Parameter pit must be non-null"); + parcAssertNotNull(interestMessage, + "Parameter interestMessage must be non-null"); + + StandardPIT *pit = pit_Closure(generic); + + if (logger_IsLoggable(pit->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(pit->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, "Message %p removed from PIT", + (void *)interestMessage); + } + + parcHashCodeTable_Del(pit->table, interestMessage); +} + +static PitEntry *_pitStandard_GetPitEntry(const PIT *generic, + const Message *interestMessage) { + parcAssertNotNull(generic, "Parameter pit must be non-null"); + parcAssertNotNull(interestMessage, + "Parameter interestMessage must be non-null"); + + StandardPIT *pit = pit_Closure(generic); + + PitEntry *entry = parcHashCodeTable_Get(pit->table, interestMessage); + if (entry) { + return pitEntry_Acquire(entry); + } + return NULL; +} + +// ====================================================================== +// Public API + +PIT *pitStandard_Create(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + + size_t allocation = sizeof(PIT) + sizeof(StandardPIT); + + PIT *generic = parcMemory_AllocateAndClear(allocation); + parcAssertNotNull(generic, "parcMemory_AllocateAndClear(%zu) returned NULL", + allocation); + generic->closure = (uint8_t *)generic + sizeof(PIT); + + StandardPIT *pit = pit_Closure(generic); + pit->forwarder = forwarder; + pit->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + + size_t initialSize = 65535; + pit->table = + parcHashCodeTable_Create_Size(hashTableFunction_MessageNameEquals, + hashTableFunction_MessageNameHashCode, NULL, + _pit_PitEntryDestroyer, initialSize); + + if (logger_IsLoggable(pit->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(pit->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, "PIT %p created", (void *)pit); + } + + generic->getPitEntry = _pitStandard_GetPitEntry; + generic->receiveInterest = _pitStandard_ReceiveInterest; + generic->release = _pitStandard_Destroy; + generic->removeInterest = _pitStandard_RemoveInterest; + generic->satisfyInterest = _pitStandard_SatisfyInterest; + + return generic; +} diff --git a/hicn-light/src/processor/pitStandard.h b/hicn-light/src/processor/pitStandard.h new file mode 100755 index 000000000..b9ba026c8 --- /dev/null +++ b/hicn-light/src/processor/pitStandard.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file pitStandard.h + * @brief The Pending Interest Table + * + * Implements the standard Pending Interest Table. + * + */ + +#ifndef pitStandard_h +#define pitStandard_h + +#include + +/** + * Creates a PIT table + * + * Creates and allocates an emtpy PIT table. The Forwarder reference is + * used for logging and for time functions. + * + * @param [in] hicn-light The releated Forwarder + * + * @return non-null a PIT table + * @return null An error + */ +PIT *pitStandard_Create(Forwarder *forwarder); +#endif // pit_h diff --git a/hicn-light/src/processor/pitVerdict.h b/hicn-light/src/processor/pitVerdict.h new file mode 100755 index 000000000..16631fa51 --- /dev/null +++ b/hicn-light/src/processor/pitVerdict.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file pitVerdict.h + * @brief Adding an entry to the PIT will return NEW or EXISTING + * + * Adding an entry to the PIT will return NEW or EXISTING + * + */ + +#ifndef pitVerdict_h +#define pitVerdict_h + +/** + * @typedef PitVerdict + * @abstract The verdit of the PIT for receiving a message + * @constant PITVerdict_Forward The message made a new PIT entry, the interest + * should be forwarded + * @constant PITVerdict_Aggregate The Interest was aggregated in the PIT, does + * not need to be forwarded + */ +typedef enum { PITVerdict_Forward, PITVerdict_Aggregate } PITVerdict; +#endif // pitVerdict_h diff --git a/hicn-light/src/socket/CMakeLists.txt b/hicn-light/src/socket/CMakeLists.txt new file mode 100755 index 000000000..6ea94dcfa --- /dev/null +++ b/hicn-light/src/socket/CMakeLists.txt @@ -0,0 +1,31 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +if (UNIX AND NOT APPLE AND NOT ANDROID_API) + list(APPEND HEADER_FILES + socket/api.h + socket/error.h + socket/ops.h + ) + + list(APPEND SOURCE_FILES + socket/api.c + socket/error.c + socket/ops_linux.c + ) +endif() + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/hicn-light/src/socket/api.c b/hicn-light/src/socket/api.c new file mode 100755 index 000000000..aede01efe --- /dev/null +++ b/hicn-light/src/socket/api.c @@ -0,0 +1,604 @@ +#include // inet_ntop +#include // '' +#include // tfind(), tdestroy(), twalk(), preorder... +#include +#include // perror +#include // calloc +#include // memcpy +#include // '' +#include // getaddrinfo +#include // close + +#include "api.h" +#include "error.h" +#include "ops.h" + +#define INET_MAX_ADDRSTRLEN INET6_ADDRSTRLEN + +#define IF_NAMESIZE 16 +#define MAX_TABLES 256 + +#define DEFAULT_INTERVAL 1000 +#define DEFAULT_IDENTIFIER "hicn" +#define DEFAULT_SOCKET_IDENTIFIER "main" +#define LOCAL_IPV6_PREFIX "fe80" + +#define LOCAL_PRIORITY 32000 + +extern hicn_socket_ops_t ops; + +/* Configuration stored as a global variable to allow access from signal + * handlers for instance */ + +static hicn_conf_t hicn_default_conf = { + .identifier = DEFAULT_IDENTIFIER, + //.format = HF_INET6_TCP +}; + +/* Global state */ +// FIXME move into helper state ? + +struct ip_rule_state_ { + char tun_name[IF_NAMESIZE]; + ip_address_t ip_address; + uint32_t table_id; + uint8_t priority; + uint8_t address_family; +}; + +struct ip_route_state_ { + char remote_ip_address[128]; // this is to big, but it is fine for now + uint8_t address_family; + uint32_t table_id; +}; + +typedef struct ip_rule_state_ ip_rule_state; +typedef struct ip_route_state_ ip_route_state; + +int punting_table_id; +uint16_t rules_counter; +uint16_t routes_counter; +static ip_rule_state rules_to_remove[MAX_TABLES]; +static ip_route_state routes_to_remove[MAX_TABLES]; + +// END FIXME + +hicn_socket_helper_t *hicn_create() { + int rc; + + punting_table_id = -1; + rules_counter = 0; + + hicn_socket_helper_t *hicn = malloc(sizeof(hicn_socket_helper_t)); + if (!hicn) { + goto ERR_MALLOC; + } + + hicn->conf = malloc(sizeof(hicn_conf_t)); + if (hicn->conf < 0) goto ERR_CONF; + memcpy(hicn->conf, &hicn_default_conf, sizeof(hicn_conf_t)); + + /* Initialize socket tree to empty */ + hicn->socket_root = NULL; + + // enable forwarding globally. Per-interface forwarding will be enabled when + // interfaces are created (TODO) + rc = ops.enable_v6_forwarding(NULL); + if (rc < 0) { + goto ERR_FW; + } + + rc = ops.enable_v4_forwarding(); + if (rc < 0) { + goto ERR_FW; + } + + // modify priority of table local + /* ip -6 rule del from all prio 0 table local */ + /* ip -6 rule add from all prio 32000 table local */ + + rc = ops.del_lo_prio_rule(NULL, AF_INET6, 0); + if (rc < 0) { + goto ERR_FW; + } + + rc = ops.del_lo_prio_rule(NULL, AF_INET, 0); + if (rc < 0) { + goto ERR_FW; + } + + rc = ops.add_lo_prio_rule(NULL, AF_INET6, LOCAL_PRIORITY); + if (rc < 0) { + goto ERR_FW; + } + + rc = ops.add_lo_prio_rule(NULL, AF_INET, LOCAL_PRIORITY); + if (rc < 0) { + goto ERR_FW; + } + + return hicn; + +ERR_FW: + free(hicn->conf); +ERR_CONF: + free(hicn); +ERR_MALLOC: + return NULL; +} + +void hicn_destroy() { + int rc; + uint16_t i; + + /* Restore default rules */ + printf("Restoring default configuration.\n"); + rc = ops.del_lo_prio_rule(NULL, AF_INET6, LOCAL_PRIORITY); + if (rc < 0) { + goto ERR; + } + + rc = ops.del_lo_prio_rule(NULL, AF_INET, LOCAL_PRIORITY); + if (rc < 0) { + goto ERR; + } + + rc = ops.add_lo_prio_rule(NULL, AF_INET6, 0); + if (rc < 0) { + goto ERR; + } + + rc = ops.add_lo_prio_rule(NULL, AF_INET, 0); + if (rc < 0) { + goto ERR; + } + + for (i = 0; i < rules_counter; i++) { + if (strcmp(rules_to_remove[i].tun_name, "NONE") != 0) { + rc = ops.del_rule(rules_to_remove[i].tun_name, + rules_to_remove[i].address_family, + rules_to_remove[i].table_id); + if (rc < 0) { + goto ERR; + } + } else { + rc = ops.del_prio_rule( + &rules_to_remove[i].ip_address, rules_to_remove[i].address_family, + rules_to_remove[i].priority, rules_to_remove[i].table_id); + if (rc < 0) { + goto ERR; + } + } + } + + for (i = 0; i < routes_counter; i++) { + rc = ops.del_out_route(routes_to_remove[i].remote_ip_address, + routes_to_remove[i].address_family, + routes_to_remove[i].table_id); + if (rc < 0) { + goto ERR; + } + } + +ERR: + if (rc < 0) printf("Unexpected exit. Some state may not be deleted.\n"); + return; +} + +void hicn_free(hicn_socket_helper_t *hicn) { + // close tun ? + free(hicn); +} + +hicn_socket_t *hicn_socket_create() { + hicn_socket_t *socket = calloc(1, sizeof(hicn_socket_t)); + if (!socket) { + goto ERR_SOCKET; + } + socket->type = HS_UNSPEC; + + return socket; + +ERR_SOCKET: + return NULL; +} + +int hicn_socket_cmp(hicn_socket_t *a, hicn_socket_t *b) { + return b->fd - a->fd; +} + +ip_address_t *hicn_socket_get_src_ip(hicn_socket_t *socket) { + if (socket->type != HS_CONNECTION) { + return NULL; + } + return &socket->connection.tun_ip_address; +} + +typedef int (*cmp_t)(const void *, const void *); + +int hicn_socket_add(hicn_socket_helper_t *hicn, hicn_socket_t *socket) { + if (!(tsearch(socket, &hicn->socket_root, (cmp_t)hicn_socket_cmp))) { + // ERROR("Could not insert field id into index"); + return -1; + } + return 0; +} + +hicn_socket_t *hicn_socket_find(hicn_socket_helper_t *hicn, int fd) { + hicn_socket_t search = { + .fd = fd, + }; + hicn_socket_t **socket = + tfind(&search, &hicn->socket_root, (cmp_t)hicn_socket_cmp); + return socket ? *socket : NULL; +} + +/******************************************************************************* + * New API + *******************************************************************************/ + +int hicn_set_local_endpoint(hicn_socket_t *socket, const char *local_ip_address, + bool allow_null) { + int rc = HICN_SOCKET_ERROR_NONE; + + if (!local_ip_address) { + if (!allow_null) { + rc = HICN_SOCKET_ERROR_SOCKET_LOCAL_NULL_ADDRESS; + } + goto end; + } + + /* local_ip_address should be a prefix with global scope in which to pick + * the locator address to use as the source. + * If we expect to pick another IP for the tun, then it needs to be of size + * less than 128. + */ + + /* Copy the local IP address inside the connection */ + rc = hicn_ip_pton(local_ip_address, &socket->connection.tun_ip_address); + if (rc < 0) { + rc = HICN_SOCKET_ERROR_SOCKET_LOCAL_REPR; + goto end; + } + +end: + return rc; +} + +// XXX This could be used by hicn_set_remote_endpoint +// XXX This has been introduced for mapme +int hicn_get_local_address(const ip_address_t *remote_address, + ip_address_t *local_address) { + int rc = 0; + uint32_t interface_id; + char remote_address_str[INET_MAX_ADDRSTRLEN]; + + rc = hicn_ip_ntop(remote_address, remote_address_str, + sizeof(remote_address_str)); + if (rc < 0) { + rc = HICN_SOCKET_ERROR_BIND_REMOTE_REPR; + goto ERR; + } + + rc = ops.get_output_ifid(remote_address_str, remote_address->family, + &interface_id); + if (rc < 0 || interface_id == 0) { + rc = HICN_SOCKET_ERROR_BIND_REMOTE_INTERFACE; + goto ERR; + } + + /* Local ip */ + rc = ops.get_ip_addr(interface_id, remote_address->family, local_address); + if (rc < 0) { + rc = HICN_SOCKET_ERROR_BIND_REMOTE_NETMASK; + goto ERR; + } + +ERR: + return rc; +} + +/** + * + * sets socket->interface_id + */ +int hicn_set_remote_endpoint(hicn_socket_t *socket, + const char *remote_ip_address) { + int af, rc = HICN_SOCKET_ERROR_NONE; + ip_address_t addr; + + af = get_addr_family(remote_ip_address); + if ((af != AF_INET6) && (af != AF_INET)) { + return HICN_SOCKET_ERROR_INVALID_IP_ADDRESS; + } + + /* Bind local endpoint if not done yet */ + if (ip_address_empty(&socket->connection.tun_ip_address)) { + char local_ip_address[INET_MAX_ADDRSTRLEN]; + + /* Local interface id */ + // INFO("Getting interface_id from gateway IP address %s", + // remote_ip_address); + ///// + int addr_family = get_addr_family(remote_ip_address); + if (addr_family < 0) { + rc = addr_family; + goto ERR; + } + + rc = ops.get_output_ifid(remote_ip_address, (uint8_t)addr_family, + &socket->connection.interface_id); + if (rc < 0 || socket->connection.interface_id == 0) { + rc = HICN_SOCKET_ERROR_BIND_REMOTE_INTERFACE; + goto ERR; + } + + /* Local ip */ + rc = ops.get_ip_addr(socket->connection.interface_id, (uint8_t)addr_family, + &addr); + if (rc < 0) { + rc = HICN_SOCKET_ERROR_BIND_REMOTE_NETMASK; + goto ERR; + } + ///// + + /* Convert to representation format */ + rc = hicn_ip_ntop(&addr, local_ip_address, sizeof(local_ip_address)); + if (rc < 0) { + rc = HICN_SOCKET_ERROR_BIND_REMOTE_REPR; + goto ERR; + } + + rc = hicn_set_local_endpoint(socket, local_ip_address, true); + if (rc < 0) { + switch (rc) { + case HICN_SOCKET_ERROR_SOCKET_LOCAL_NULL_ADDRESS: + rc = HICN_SOCKET_ERROR_BIND_REMOTE_LOCAL_NULL_ADDR; + break; + case HICN_SOCKET_ERROR_SOCKET_LOCAL_REPR: + rc = HICN_SOCKET_ERROR_BIND_REMOTE_LOCAL_REPR; + break; + case HICN_SOCKET_ERROR_SOCKET_LOCAL_HEURISTIC: + rc = HICN_SOCKET_ERROR_BIND_REMOTE_LOCAL_HEURISTIC; + break; + case HICN_SOCKET_ERROR_SOCKET_LOCAL_SET_TUN_IP: + rc = HICN_SOCKET_ERROR_BIND_REMOTE_LOCAL_SET_TUN_IP; + break; + } + goto ERR; + } + } + return HICN_SOCKET_ERROR_NONE; + +ERR: + return rc; +} + +/** + * + * We need at least an identifier. + */ +int hicn_socket(hicn_socket_helper_t *hicn, const char *identifier, + const char *local_ip_address) { + int rc; + + hicn_socket_t *socket = hicn_socket_create(); + if (!socket) { + rc = -5; + goto ERR_SOCKET; + } + + ops.get_tun_name(hicn->conf->identifier, identifier, socket->tun_name); + + // register the hicn face on which to bind prefixes, create the in/out TUN + // device + socket->fd = ops.tun_create(socket->tun_name); + if (socket->fd <= 0) { + rc = -2; + goto ERR_TUN; + } + + // INFO("Successfully created listener on TUN device %s", socket->tun_name); + + /* Retrieve interface id */ + socket->tun_id = ops.get_ifid(socket->tun_name); + if (socket->tun_id < 0) { + rc = -3; + goto ERR_TUNIFID; + } + // INFO("Interface id=%d", socket->tun_id); + + // WARN("Need to set offload"); + + // INFO("Setting interface up"); + rc = ops.up_if(socket->tun_id); + if (rc < 0) { + rc = -4; + goto ERR_UP; + } + + /* Update state */ + rc = hicn_socket_add(hicn, socket); + if (rc < 0) { + rc = -5; + goto ERR_ADD; + } + + rc = hicn_set_local_endpoint(socket, local_ip_address, true); + if (rc < 0) { + rc = -6; + goto ERR_ADJACENCY; + } + + return socket->fd; + +ERR_ADJACENCY: +ERR_ADD: +ERR_UP: +ERR_TUNIFID: +ERR_TUN: + free(socket); +ERR_SOCKET: + // ERR_PARAMS: + return rc; +} + +int hicn_listen(hicn_socket_helper_t *hicn, int fd, const char *prefix) { + int rc; + hicn_socket_t *socket = hicn_socket_find(hicn, fd); + if (!socket) { + return -1; + } + + /* Check socket is not a connection */ + if (socket->type == HS_CONNECTION) { + return -1; + } + + rc = ops.add_in_route_s(prefix, socket->tun_id); + if (rc < 0) { + return rc; + } + + ip_address_t ip_address; + rc = hicn_ip_pton(prefix, &ip_address); + if (rc < 0) { + return rc; + } + + // ip -6 rule add from b001::/16 prio 0 table 100 + socket->connection.table_id = + socket->tun_id % MAX_TABLES; // this table should be unused + + if (punting_table_id == -1) punting_table_id = socket->connection.table_id; + + rc = ops.add_prio_rule(&ip_address, ip_address.family, 0, + socket->connection.table_id); + if (rc < 0) { + return rc; + } + + strcpy(rules_to_remove[rules_counter].tun_name, "NONE"); + + rules_to_remove[rules_counter].ip_address = ip_address; + rules_to_remove[rules_counter].address_family = ip_address.family; + rules_to_remove[rules_counter].table_id = socket->connection.table_id; + rules_to_remove[rules_counter].priority = 0; + ++rules_counter; + + /* Update socket upon success */ + socket->type = HS_LISTENER; + + return 0; +} + +/** + * + * We can pass all adjacency parameters but identifier + */ +int hicn_bind(hicn_socket_helper_t *hicn, int fd, + const char *remote_ip_address) { + // uint32_t interface_id; + int rc = HICN_SOCKET_ERROR_NONE; + + hicn_socket_t *socket = hicn_socket_find(hicn, fd); + if (!socket) { + rc = HICN_SOCKET_ERROR_BIND_SOCKET_NOT_FOUND; + goto ERR; + } + + /* We allow reuse */ + if (socket->type == HS_CONNECTION) return rc; + + /* Check socket is not a connection */ + if (socket->type != HS_UNSPEC) { + rc = HICN_SOCKET_ERROR_BIND_SOCKET_ALREADY_BOUND; + goto ERR; + } + socket->type = HS_CONNECTION; + + // each connection is associated a table id, let's take it equal to the + // tun ID by default (% MAX_TABLES, assuming TUN IDs do not overlap modulo + // 256...). + // XXX we need to make sure the corresponding table is flushed. + socket->connection.table_id = + socket->tun_id % MAX_TABLES; // interface_id; // ops.get_free_table_id(); + + // XXX use IP address + rc = hicn_set_remote_endpoint(socket, remote_ip_address); + if (rc < 0) { + goto ERR; + } + + // rule + // ip -6 rule from all iif eth0 lookup 200 + // INFO("Adding output rule for %s in table %d", socket->tun_name, + // socket->connection.table_id); + int addr_family = get_addr_family(remote_ip_address); + if (addr_family < 0) { + rc = addr_family; + goto ERR; + } + + rc = ops.add_rule(socket->tun_name, (uint8_t)addr_family, + socket->connection.table_id); + if (rc < 0) { + rc = HICN_SOCKET_ERROR_BIND_RULE; + goto ERR; + } + + strcpy(rules_to_remove[rules_counter].tun_name, socket->tun_name); + rules_to_remove[rules_counter].address_family = addr_family; + rules_to_remove[rules_counter].table_id = socket->connection.table_id; + ++rules_counter; + + // route + // ip -6 route add default via 2002::2 table 28 + // INFO("Adding output route in table %d via gateway %s", + // socket->connection.table_id, + // remote_ip_address); + + // if the address is an IPv6 and start with fe80 we need to specify the device + // in the route + u32 default_interface = ~0; + if (addr_family == AF_INET6 && strncmp(LOCAL_IPV6_PREFIX, remote_ip_address, + strlen(LOCAL_IPV6_PREFIX)) == 0) { + rc = ops.get_output_ifid(remote_ip_address, (uint8_t)addr_family, + &default_interface); + if (rc < 0) { + goto ERR; + } + } + + rc = ops.add_out_route(remote_ip_address, (uint8_t)addr_family, + socket->connection.table_id, default_interface); + if (rc < 0) { + rc = HICN_SOCKET_ERROR_BIND_ROUTE; + goto ERR; + } + + strcpy(routes_to_remove[routes_counter].remote_ip_address, remote_ip_address); + routes_to_remove[routes_counter].table_id = socket->connection.table_id; + routes_to_remove[routes_counter].address_family = (uint8_t)addr_family; + ++routes_counter; + + // add route for data + // ip -6 route add 0:1::/64 dev hicn-if0 table 100 + // this routes are deleted by removing the tun interfaces + + if (punting_table_id == -1) { + // the punting_table_id was not initialized beacause no main-tun was created + // we use as an id (socket->tun_id - 1) % MAX_TABLES, so that we will hava a + // collision only after 255 new interfaces + punting_table_id = (socket->tun_id - 1) % MAX_TABLES; + } + rc = ops.add_in_route_table(&socket->connection.tun_ip_address, + socket->tun_id, punting_table_id); + if (rc < 0) { + rc = HICN_SOCKET_ERROR_BIND_ROUTE; + goto ERR; + } + +ERR: + return rc; +} diff --git a/hicn-light/src/socket/api.h b/hicn-light/src/socket/api.h new file mode 100755 index 000000000..e1516ebe1 --- /dev/null +++ b/hicn-light/src/socket/api.h @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file hicn_face.h + * @brief hICN socket library + * + * This module provides an interface to managing so-called hICN sockets, + * realizing punting of interest and data packets using a TUN device. + */ + +#ifndef HICN_SOCKET_API_H +#define HICN_SOCKET_API_H + +#include // uint*_t +#include + +#include +#include "error.h" + +#define BUFSIZE 4096 +#define MAX_CONNECTIONS \ + 255 // We currently limit the number of connections we can establish +#define IF_NAMESIZE 16 + +/* hICN socket helper */ + +/** hICN configuration options */ +typedef struct { + // uint32_t interval; + + /* Identifier used to name hICN TUN interfaces (should be unique) */ + char *identifier; + // hicn_format_t format; + +} hicn_conf_t; + +/** + * hICN adjacency + */ +typedef struct { + char *local_ip_address; + char *gateway_ip_address; +} hicn_adjacency_t; + +#define EMPTY_HICN_ADJACENCY \ + (hicn_adjacency_t) { 0, 0 } + +/* hICN socket operations */ + +typedef struct { + uint8_t pkbuf[BUFSIZE]; + uint32_t rb_pkbuf_r; + uint32_t rb_pkbuf_w; +} hicn_buffer_t; + +typedef enum { HS_UNSPEC, HS_LISTENER, HS_CONNECTION } hicn_socket_type_t; + +typedef struct hicn_socket_s { + hicn_socket_type_t type; + int fd; + + /* Implementation specific state follows */ + char tun_name[IF_NAMESIZE]; + uint32_t tun_id; + + hicn_buffer_t buffer; + void (*cb)(struct hicn_socket_s *, void *, uint8_t *, size_t); + void *cb_data; + + union { + struct { + ip_address_t tun_ip_address; + uint32_t interface_id; + + /* ID of the corresponding table : avoid default values of 0, 32766 and + * 32767 */ + uint8_t table_id; + } connection; + }; +} hicn_socket_t; + +/** + * hICN global state + */ +typedef struct { + /* Configuration data */ + hicn_conf_t *conf; + + // We need state associate to each FD, to know what type of socket it is and + // its state. + void *socket_root; /**< A tree of socket indexed by their fd */ + +} hicn_socket_helper_t; + +/** + * Create an hICN instance. + * + * This is used to configure the state of an hICN router consistently between + * a listener and the different connections. It also regroups all the state + * related to hICN functionalities. + * + * @return A pointer to an hICN instance. + */ +hicn_socket_helper_t *hicn_create(); + +void hicn_destroy(); + +/** + * Retrieve hICN configuration. + * + * Gets the current configuration of an hICN instance for information purposes, + * or later update it. + * + * TODO + * - We might want to prevent configuration updates while the hICN instance is + * running. Define running... + * + * @param [in] hicn Pointer to hICN instance. + * @return Pointer to an hICN configuration data structure. + * + * @see hicn_set_conf + */ +hicn_conf_t *hicn_get_conf(hicn_socket_helper_t *hicn); + +/** + * Update hICN configuration. + * + * @param [in] hicn Pointer to an hICN instance. + * @param [in] hicn_conf Pointer to an hICN configuration data structure. + * @return 0 in case of success, -1 otherwise. + * + * @see hicn_get_conf + */ +int hicn_set_conf(hicn_socket_helper_t *hicn, hicn_conf_t *hicn_conf); + +/** + * Release hICN state. + * + * @param [in] hicn Pointer to an hICN instance. + */ +void hicn_free(hicn_socket_helper_t *hicn); + +// FIXME doc +int hicn_get_local_address(const ip_address_t *remote_address, + ip_address_t *local_address); + +/* hICN socket */ + +/** + * Create an hICN socket. + * + * An hICN socket abstracts the underlying implementation and allows hICN + * packets to be sent and received independently of the underlying + * implementation. + * + * It is possible to further specialize the socket in a listener socket, and a + * connection socket. + * + * @param [in] hicn Pointer to an hICN instance. + * @param [in] identifier Unique identifier for this socket, used to named the + * TUN device + * @param [in] local_ip_address IP address used locally by the socket (or NULL + * for letting the library decide automatically). + * @return File descriptor (>0) in case of success, -1 otherwise. + * + * @see hicn_listen + * @see hicn_bind + */ +int hicn_socket(hicn_socket_helper_t *hicn, const char *identifier, + const char *local_ip_address); + +/** + * Packet punting. + * + * Note that we cannot listen on a socket that is already bound. + * + * @param [in] hicn Pointer to an hICN instance. + * @param [in] fd File descriptor identifying the hICN socket. + * @param [in] prefix Prefix (IPv4 or IPv6) to be bound to hICN in + * RFC-compliant presentation format. + * @return 0 in case of success, -1 otherwise. + * + * @see hicn_socket + */ +int hicn_listen(hicn_socket_helper_t *hicn, int fd, const char *prefix); + +/** + * Packet forwarding + * @param [in] hicn Pointer to an hICN instance. + * @param [in] fd File descriptor identifying the hICN socket. + * @param [in] prefix Prefix (IPv4 or IPv6) to be bound to hICN in + * RFC-compliant presentation format. + * @return 0 in case of success, -1 otherwise. + * + * XXX adjacency does not perform any copy heresofar + * + * @see hicn_socket + */ +int hicn_bind(hicn_socket_helper_t *hicn, int fd, + const char *remote_ip_address); + +#endif /* HICN_SOCKET_API_H */ diff --git a/hicn-light/src/socket/error.c b/hicn-light/src/socket/error.c new file mode 100755 index 000000000..3dafec8cf --- /dev/null +++ b/hicn-light/src/socket/error.c @@ -0,0 +1,7 @@ +#include "error.h" + +const char* HICN_SOCKET_ERROR_STRING[] = { +#define _(a, b, c) [b] = c, + foreach_hicn_socket_error +#undef _ +}; diff --git a/hicn-light/src/socket/error.h b/hicn-light/src/socket/error.h new file mode 100755 index 000000000..8195efd84 --- /dev/null +++ b/hicn-light/src/socket/error.h @@ -0,0 +1,46 @@ +#ifndef HICN_SOCKET_ERROR_H +#define HICN_SOCKET_ERROR_H + +// FIXME remove unused errors +#define foreach_hicn_socket_error \ + _(NONE, 0, "OK") \ + _(UNSPEC, 1, "unspecified error") \ + _(NOT_HICN, 2, "not a hICN paclet") \ + _(UNKNOWN_ADDRESS, 10, "unknown address") \ + _(INVALID_PARAMETER, 20, "invalid parameter") \ + _(INVALID_IP_ADDRESS, 21, "invalid IP address") \ + _(CORRUPTED_PACKET, 22, "corrupted packet") \ + _(UNEXPECTED, 98, "unexpected error") \ + _(NOT_IMPLEMENTED, 99, "not implemented") \ + _(SOCKET_LOCAL_NULL_ADDRESS, 101, "empty local address") \ + _(SOCKET_LOCAL_REPR, 102, "cannot represent local address") \ + _(SOCKET_LOCAL_HEURISTIC, 103, "error finding local address") \ + _(SOCKET_LOCAL_SET_TUN_IP, 104, "cannot set local IP to TUN") \ + _(BIND_SOCKET_NOT_FOUND, 301, "bind: socket not found") \ + _(BIND_SOCKET_ALREADY_BOUND, 302, "bind: socket already bound") \ + _(BIND_REMOTE_INTERFACE, 303, "bind: no interface towards gateway") \ + _(BIND_REMOTE_NETMASK, 304, "bind: no local IP with netmask < 128") \ + _(BIND_REMOTE_REPR, 305, "bind: error representing local IP") \ + _(BIND_REMOTE_LOCAL_NULL_ADDR, 306, "bind: could not set local endpoint") \ + _(BIND_REMOTE_LOCAL_REPR, 307, "bind: error representing remote IP") \ + _(BIND_REMOTE_LOCAL_HEURISTIC, 308, "bind: could not apply heuristic") \ + _(BIND_REMOTE_LOCAL_SET_TUN_IP, 309, "bind: error setting local IP to TUN") \ + _(BIND_NDP, 310, "bind: could not enable NDP proxy") \ + _(BIND_NEIGH_PROXY, 311, "bind: could not neighbour") \ + _(BIND_REPR, 312, "bind: error represeting IP") \ + _(BIND_LO, 313, "bind: could not remove local route") \ + _(BIND_RULE, 314, "bind: could not add rule") \ + _(BIND_ROUTE, 315, "bind: could not add output route") + +typedef enum { +#define _(a, b, c) HICN_SOCKET_ERROR_##a = (-b), + foreach_hicn_socket_error +#undef _ + HICN_SOCKET_N_ERROR, +} hicn_socket_error_t; + +extern const char *HICN_SOCKET_ERROR_STRING[]; + +#define hicn_socket_strerror(errno) (char *)(HICN_SOCKET_ERROR_STRING[-errno]) + +#endif /* HICN_SOCKET_ERROR_H */ diff --git a/hicn-light/src/socket/ops.h b/hicn-light/src/socket/ops.h new file mode 100755 index 000000000..249caf87a --- /dev/null +++ b/hicn-light/src/socket/ops.h @@ -0,0 +1,54 @@ +#ifndef HICN_SOCKET_OPS_H +#define HICN_SOCKET_OPS_H + +#include +#include + +typedef struct { + char *arch; + int (*tun_create)(char *name); + int (*get_tun_name)(const char *prefix, const char *identifier, + char *tun_name); + int (*enable_v6_forwarding)(char *interface_name); + int (*enable_v4_forwarding)(); + int (*enable_ndp_proxy)(); + + uint32_t (*get_ifid)(const char *ifname); + int (*get_output_ifid)(const char *ip_address, uint8_t address_family, + uint32_t *interface_id); + int (*get_ip_addr)(uint32_t interface_id, uint8_t address_family, + ip_address_t *ip_address); + int (*set_ip_addr)(uint32_t interface_id, ip_address_t *ip_address); + int (*up_if)(uint32_t interface_id); + int (*add_in_route_table)(const ip_address_t *prefix, + const uint32_t interface_id, + const uint8_t table_id); + int (*add_in_route_table_s)(const char *prefix, const uint32_t interface_id, + const uint8_t table_id); + int (*add_in_route_s)(const char *prefix, const uint32_t interface_id); + int (*add_out_route)(const char *gateway, const uint8_t address_family, + const uint8_t table_id, int default_route); + int (*del_out_route)(const char *gateway, const uint8_t address_family, + const uint8_t table_id); + int (*del_lo_route)(const ip_address_t *ip_address); + int (*add_rule)(const char *interface_name, const uint8_t address_family, + const uint8_t table_id); + int (*del_rule)(const char *interface_name, const uint8_t address_family, + const uint8_t table_id); + int (*add_neigh_proxy)(const ip_address_t *ip_address, + const uint32_t interface_id); + int (*add_prio_rule)(const ip_address_t *ip_address, + const uint8_t address_family, const uint32_t priority, + const uint8_t table_id); + int (*add_lo_prio_rule)(const ip_address_t *ip_address, + const uint8_t address_family, + const uint32_t priority); + int (*del_prio_rule)(const ip_address_t *ip_address, + const uint8_t address_family, const uint32_t priority, + const uint8_t table_id); + int (*del_lo_prio_rule)(const ip_address_t *ip_address, + const uint8_t address_family, + const uint32_t priority); +} hicn_socket_ops_t; + +#endif /* HICN_SOCKET_OPS_H */ diff --git a/hicn-light/src/socket/ops_linux.c b/hicn-light/src/socket/ops_linux.c new file mode 100755 index 000000000..d085f0d3d --- /dev/null +++ b/hicn-light/src/socket/ops_linux.c @@ -0,0 +1,1723 @@ +#include // ioctl +#include // needed by linux/if.h +//#include +#include +#include // '' +#include +#include // PATH_MAX +#include // fprintf +#include // memset +#include // open +#include // writev +#include // close + +#include "error.h" +#include "ops.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) + +/****************************************************************************** + * netlink.h + ******************************************************************************/ + +#ifndef HICN_NETLINK_H +#define HICN_NETLINK_H + +#include +#include + +// DEPRECATED|/* Socket */ +// DEPRECATED|int _nl_get_socket(); +// DEPRECATED|int _nl_send(int s, uint8_t * buffer, size_t len); +// DEPRECATED|size_t _nl_receive(uint8_t * buffer, size_t len); +// DEPRECATED| +// DEPRECATED|/* Netlink packet format */ +// DEPRECATED|int _nl_header(int request, uint8_t * buffer, size_t len, uint32_t +// flags); DEPRECATED|int _nl_payload_rule(uint8_t table_id, uint8_t * buffer, +// size_t len); DEPRECATED|int _nl_payload_link(uint32_t ifindex, uint8_t * +// buffer, size_t len); DEPRECATED|int _nl_payload_route(uint8_t table_id, +// uint8_t dst_len, uint8_t * buffer, size_t len); DEPRECATED| DEPRECATED|int +// _nl_parse(uint8_t * buffer, size_t len); DEPRECATED|int _nl_parse_ret(uint8_t +// * buffer, size_t len); DEPRECATED|int _nl_parse_link_ifid(uint8_t * buffer, +// size_t len, uint32_t * interface_id); DEPRECATED|int +// _nl_parse_link_ip_addr(uint8_t * buffer, size_t len, struct in6_addr * addr); + +/* Public interface */ + +/** + * Get the interface ID of an interface by its name + * + * @return 32-bit interface identifier in case of success, or 0. + * + * @see if_nametoindex + * + */ +uint32_t _nl_get_ifid(const char *ifname); + +/** + * Retrieve the output interface corresponding to the specified IP address. + * + * @param [in] addr IP(v6) address in presentation form. + * @param [out] Identifier of the corresponding output interface. + * @return int 0 in case of success, -1 otherwise + */ +int _nl_get_output_ifid(const char *ip_address, uint8_t address_family, + uint32_t *interface_id); + +/** + * Retrieve the first IP address of an interface (identified by its id) which + * has a netmask < 128. + * + * @param [in] s File descriptor of the netlink socket (deprecated). + * @param [in] interface_id Identifier of the interface for which to retrieve + * the IP address. + * @param [out] addr IP(v6) address in binary form. + * @return int 0 in case of success, -1 otherwise + * + * @see getifaddrs + */ +int _nl_get_ip_addr(uint32_t interface_id, uint8_t address_family, + ip_address_t *ip_address); + +int _nl_set_ip_addr(uint32_t interface_id, ip_address_t *ip_address); + +int _nl_up_if(uint32_t interface_id); + +int _nl_add_in_route_table(const ip_address_t *prefix, + const uint32_t interface_id, const uint8_t table_id); +int _nl_add_in_route_table_s(const char *prefix, const uint32_t interface_id, + const uint8_t table_id); +int _nl_add_in_route_s(const char *prefix, const uint32_t interface_id); + +int _nl_add_out_route(const char *gateway, const uint8_t address_family, + const uint8_t table_idi, int default_route); +int _nl_del_out_route(const char *gateway, const uint8_t address_family, + const uint8_t table_id); + +int _nl_del_lo_route(const ip_address_t *ip_address); + +int _nl_add_rule(const char *interface_name, const uint8_t address_family, + const uint8_t table_id); +int _nl_del_rule(const char *interface_name, const uint8_t address_family, + const uint8_t table_id); + +int _nl_add_neigh_proxy(const ip_address_t *ip_address, + const uint32_t interface_id); + +int _nl_add_prio_rule(const ip_address_t *ip_address, + const uint8_t address_family, const uint32_t priority, + const uint8_t table_id); +int _nl_add_lo_prio_rule(const ip_address_t *ip_address, + const uint8_t address_family, const uint32_t priority); +int _nl_del_prio_rule(const ip_address_t *ip_address, + const uint8_t address_family, const uint32_t priority, + const uint8_t table_id); +int _nl_del_lo_prio_rule(const ip_address_t *ip_address, + const uint8_t address_family, const uint32_t priority); + +#endif /* HICN_NETLINK_H */ + +/****************************************************************************** + * netlink.c + ******************************************************************************/ + +/* + * This module offers an interface to the Netlink API appropriate for + * implementing punting as required by hICN (1). + * + * More specifically, it consists of the following functionalities: + * - LINK + . map interface name to ID + . set and interface up + * - ADDR + . get and set ip addresses on a given interface ID + * - ROUTE + . get output interface id towards IP (ip route get IP > interface_id) + . add input route (ip route add PREFIX dev INTERFACE) for punting + interests . add output route (ip route add default GATEWAY table TABLE) for + routing interests (2, 3) . delete local route towards IP (ip route del IP table + local) for ??? + /!\ could this be avoided by removing the local attribute in the + netlink call ? + * - RULE + * . add output rule (ip rule add iif interface table TABLE) for routing + interests (2, 3) + * - ND PROXY + * . enable NDP proxy functionality for IP on interface ID (ip -6 neigh add + proxy IP dev INTERFACE) + * for allowing the TUN to be reachable on the reverse data path + * + * Implementation notes: + * (1) We have not been using the libnl library because it requires + * manipulating too many function and data structures for a simple purpose. + * Currently, many parts of the code are somehow repetitive, but this might + * be improved by a proper API in a future release. + * (2) allows load balancing over different interfaces = multihoming. Please + * note that it is not possible to have load balancing over two faces using + * the same output interface as we are using the underlying IP network ! + * This might be mitigated with the use of SR however. + * (3) The implementation of punting heavily uses the policy routing + * functionalities, as we need to hook through a TUN into user space a + * whole prefix used as a destination (for interests) or source (for data + * packets). We thus combine the use of rules to assign routing table IDs, + * and routes inside those tables. As there is no easy way to allocate + * which routing tables we use, we made the choice to index them by the ID + * of the interface, assuming there is no external conflict. This might be + * improved in the future. + * + * This hICN implementation uses TUNs in two different ways: + * - a main TUN interface, which receives all punted interests, + * demultiplex them before assigning them an input face (eventually + * dynamically creating it); + * - a set of output TUN interfaces, aka faces, used for routing of + * interests, and for receiving the corresponding data packets on the way + * back. Punting of data packets if based of their destination IP, which + * is the IP of the physical output interface used for the interest, which + * is unique (cf (2)). + * + * The corresponding routing tables IDs are : + * MAIN_TUN_ID -> used for punting of data packets + * OUTPUT_TUN_ID_i -> used for routing of interests towards next hop + * (bypassing local IP routing table) + * + * Note that punting of interests is done just through a route, and routing + * of data packets is done just through the regular IP routing table on the + * note after the address translation done in the forwarder. + * + * - Forging netlink packets + * + * A previous implementation used function calls with pointers to populate + * the various header parts in a buffer in order to build a netlink packet. + * A newer implementation uses nested structs and iovecs to build the whole + * packet in a single write call. This should allow a simpler evolution + * towards a cleaner API. + */ + +#include // inet_pton +#include // errno +#include // fib_rule_hdr, FRA_* +#include +#include +#include // IFF_UP +#include // in6addr +#include // perror +#include +#include // '' +#include // socket +#include // read + +#include // '' +#include // send, recv + +//#include "../../hicn.h" +//#include "../../hicn_util.h" // ARRAY_SIZE, hicn_packet_dump_iov + +#define BUFSIZE 4096 +#define FLAGS_CREATE NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK +// ?? +#define FLAGS_CREATE_MATCH \ + NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK | NLM_F_MATCH + +// XXX putting ACK poses a prolem for the value received by get_if_id. +#define FLAGS_GET NLM_F_REQUEST +#define FLAGS_GET_ROOT (NLM_F_REQUEST | NLM_F_ROOT) + +#define FLAGS_LIST NLM_F_REQUEST | NLM_F_DUMP + +#define IF_NAMESIZE 16 +#define FR_ACT_TO_TBL 1 +#define NLMSG_BOTTOM(nlmsg) \ + ((struct rtattr *)(((void *)(nlmsg)) + NLMSG_ALIGN((nlmsg)->nlmsg_len))) + +int seq = 1; + +static inline size_t iov_length(const struct iovec *iov, + unsigned long nr_segs) { + unsigned long seg; + size_t ret = 0; + + for (seg = 0; seg < nr_segs; seg++) ret += iov[seg].iov_len; + return ret; +} + +typedef struct { + struct nlmsghdr hdr; + struct nlmsgerr payload; +} nl_err_hdr_t; + +/* Low level : nl header */ + +int _nl_get_socket() { return socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); } + +int _nl_header(int request, uint8_t *buffer, size_t len, uint32_t flags) { + struct nlmsghdr *nl = (struct nlmsghdr *)buffer; + + nl->nlmsg_len = 0; // NLMSG_LENGTH(sizeof(struct ifinfomsg)); + nl->nlmsg_type = request; + nl->nlmsg_flags = flags; + nl->nlmsg_seq = seq++; // + nl->nlmsg_pid = 0; // getpid(); + + return 0; +} + +/* Low level : nl protocols */ + +/* Low level : attributes */ + +int addAttr(struct nlmsghdr *nl, int maxlen, int type, void *data, + int attr_len) { + struct rtattr *rta; + int len = RTA_LENGTH(attr_len); + + if (NLMSG_ALIGN(nl->nlmsg_len) + len > maxlen) { + exit(EXIT_FAILURE); + } + + rta = (struct rtattr *)((char *)nl + NLMSG_ALIGN(nl->nlmsg_len)); + rta->rta_type = type; + rta->rta_len = len; + memcpy(RTA_DATA(rta), data, attr_len); + nl->nlmsg_len = NLMSG_ALIGN(nl->nlmsg_len) + len; + return 0; +} + +int _nl_payload_rule(uint8_t table_id, uint8_t address_family, uint8_t *buffer, + size_t len) { + struct nlmsghdr *nl = (struct nlmsghdr *)buffer; + struct fib_rule_hdr *frh = (struct fib_rule_hdr *)(NLMSG_DATA(buffer)); + + memset(frh, 0, sizeof(struct fib_rule_hdr)); + frh->family = address_family; + frh->table = table_id; + frh->action = FR_ACT_TO_TBL, + frh->flags = NLM_F_REPLACE; // 0 + frh->tos = 0; + + nl->nlmsg_len += NLMSG_LENGTH(sizeof(struct fib_rule_hdr)); + + return 0; +} + +int _nl_payload_link(uint32_t ifindex, uint8_t *buffer, size_t len) { + struct nlmsghdr *nl = (struct nlmsghdr *)buffer; + struct ifinfomsg *ifi = (struct ifinfomsg *)(NLMSG_DATA(buffer)); + + memset(ifi, 0, sizeof(struct ifinfomsg)); + ifi->ifi_family = AF_UNSPEC; + // ifi->ifi_type = 0; + ifi->ifi_index = + ifindex; // new interface, could be specified since linux 3.7 + ifi->ifi_flags = 0; + // ifi->ifi_change = 0xffffffff; + + nl->nlmsg_len += NLMSG_LENGTH(sizeof(struct ifinfomsg)); + + return 0; +} + +int _nl_payload_addr(uint32_t ifindex, uint8_t *buffer, size_t len) { + struct nlmsghdr *nl = (struct nlmsghdr *)buffer; + struct ifaddrmsg *addr = (struct ifaddrmsg *)(NLMSG_DATA(buffer)); + + memset(addr, 0, sizeof(struct ifaddrmsg)); + addr->ifa_family = AF_UNSPEC; // INET6; + /* + addr->ifa_prefixlen = 128; + addr->ifa_flags = 0; + addr->ifa_scope = RT_SCOPE_LINK; //IFA_ADDRESS; + addr->ifa_index = ifindex; + */ + + nl->nlmsg_len += NLMSG_LENGTH(sizeof(struct ifaddrmsg)) - 4; + + return 0; +} + +int _nl_payload_route(uint8_t table_id, uint8_t addr_family, uint8_t dst_len, + uint8_t *buffer, size_t len) { + struct nlmsghdr *nl = (struct nlmsghdr *)buffer; + struct rtmsg *raddr = (struct rtmsg *)(NLMSG_DATA(buffer)); + + raddr->rtm_family = addr_family; + raddr->rtm_dst_len = dst_len; + raddr->rtm_src_len = 0; + raddr->rtm_tos = 0; + + raddr->rtm_table = table_id; + raddr->rtm_protocol = RTPROT_BOOT; + raddr->rtm_scope = RT_SCOPE_UNIVERSE; + raddr->rtm_type = RTN_UNICAST; + + raddr->rtm_flags = 0; + + nl->nlmsg_len += NLMSG_LENGTH(sizeof(struct rtmsg)); + + return 0; +} + +uint32_t _nl_get_ifid(const char *interface_name) { + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + size_t n; + int fd; + size_t len = interface_name ? strlen(interface_name) + 1 : 0; + uint8_t padding[RTA_ALIGNTO] = {0, 0, 0, 0}; + + if (len == 0) { + goto ERR_IF; + } + + struct { + struct nlmsghdr hdr; + struct ifinfomsg payload; + } msg = {//.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .hdr.nlmsg_type = RTM_GETLINK, + .hdr.nlmsg_flags = FLAGS_GET, + .payload.ifi_family = AF_UNSPEC, + .payload.ifi_index = 0}; + struct rtattr a_ifname = {RTA_LENGTH(strlen(interface_name) + 1), + IFLA_IFNAME}; + + struct iovec iov[] = {{&msg, sizeof(msg)}, + {&a_ifname, sizeof(a_ifname)}, + {(char *)interface_name, len}, + {padding, RTA_SPACE(len) - RTA_LENGTH(len)}}; + msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR_SOCKET; + } + n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); + if (n == -1) { + goto ERR_SEND; + } + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR_RECV; + } + + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR_NL; + } + return 0; /* Unexpected */ + } + + for (; NLMSG_OK(hdr, n); hdr = NLMSG_NEXT(hdr, n)) { + struct ifinfomsg *payload = (struct ifinfomsg *)NLMSG_DATA(hdr); + return payload->ifi_index; + } + return 0; + +ERR_NL: +ERR_RECV: +ERR_SEND: +ERR_SOCKET: +ERR_IF: + return 0; +} + +int _nl_get_output_ifid(const char *ip_address, uint8_t family_address, + uint32_t *interface_id) { + int rc; + + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + size_t n; + int fd; + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR; + } + + if (family_address == AF_INET6) { + struct in6_addr addr; // V6SPECIFIC + + struct { + struct nlmsghdr hdr; + struct rtmsg payload; + } msg = { + .hdr.nlmsg_type = RTM_GETROUTE, + .hdr.nlmsg_flags = NLM_F_REQUEST, + .hdr.nlmsg_seq = seq++, + .payload.rtm_family = AF_INET6, + .payload.rtm_dst_len = IPV6_ADDR_LEN_BITS, + .payload.rtm_src_len = 0, + .payload.rtm_tos = 0, + .payload.rtm_table = RT_TABLE_UNSPEC, + .payload.rtm_protocol = RTPROT_UNSPEC, + .payload.rtm_scope = RT_SCOPE_UNIVERSE, + .payload.rtm_type = RTN_UNSPEC, + .payload.rtm_flags = 0 // RTM_F_NOTIFY in 'ip route get' + }; + + /* Convert the IP address to binary form */ + rc = inet_pton(AF_INET6, ip_address, &addr); + if (rc <= 0) { + goto ERR; + } + + /* Set attribute = length/type/value */ + struct rtattr a_dst = {RTA_LENGTH(16), RTA_DST}; + struct iovec iov[] = { + {&msg, sizeof(msg)}, + {&a_dst, sizeof(a_dst)}, // attribute + {&addr, sizeof(addr)} // value + }; + msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); + + n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); + if (n == -1) { + goto ERR; + } + } else if (family_address == AF_INET) { + struct in_addr addr; + + struct { + struct nlmsghdr hdr; + struct rtmsg payload; + } msg = { + .hdr.nlmsg_type = RTM_GETROUTE, + .hdr.nlmsg_flags = NLM_F_REQUEST, + .hdr.nlmsg_seq = seq++, + .payload.rtm_family = AF_INET, + .payload.rtm_dst_len = IPV4_ADDR_LEN_BITS, + .payload.rtm_src_len = 0, + .payload.rtm_tos = 0, + .payload.rtm_table = RT_TABLE_UNSPEC, + .payload.rtm_protocol = RTPROT_UNSPEC, + .payload.rtm_scope = RT_SCOPE_UNIVERSE, + .payload.rtm_type = RTN_UNSPEC, + .payload.rtm_flags = 0 // RTM_F_NOTIFY in 'ip route get' + }; + + /* Convert the IP address to binary form */ + rc = inet_pton(AF_INET, ip_address, &addr); + if (rc <= 0) { + goto ERR; + } + + /* Set attribute = length/type/value */ + struct rtattr a_dst = {RTA_LENGTH(4), RTA_DST}; + struct iovec iov[] = { + {&msg, sizeof(msg)}, + {&a_dst, sizeof(a_dst)}, // attribute + {&addr, sizeof(addr)} // value + }; + msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); + + n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); + if (n == -1) { + goto ERR; + } + } else { + goto ERR; + } + + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR; + } + + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR; + } + return HICN_SOCKET_ERROR_UNEXPECTED; /* Unexpected */ + } + + for (; NLMSG_OK(hdr, n); hdr = NLMSG_NEXT(hdr, n)) { + struct rtmsg *rtm = (struct rtmsg *)NLMSG_DATA(hdr); + int attrlen = RTM_PAYLOAD(hdr); + struct rtattr *rta; + for (rta = RTM_RTA(rtm); RTA_OK(rta, attrlen); + rta = RTA_NEXT(rta, attrlen)) { + if (rta->rta_type == RTA_OIF) { + *interface_id = *(uint32_t *)RTA_DATA(rta); + return HICN_SOCKET_ERROR_NONE; + } + } + } + + return HICN_SOCKET_ERROR_NONE; + +ERR: + return HICN_SOCKET_ERROR_UNSPEC; +} + +int _nl_get_ip_addr(uint32_t interface_id, uint8_t address_family, + ip_address_t *ip_address) { + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + size_t n; + int fd; + + struct { + struct nlmsghdr hdr; + struct ifaddrmsg payload; + } msg = {.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)), + .hdr.nlmsg_type = RTM_GETADDR, + .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT, // | NLM_F_MATCH, + .payload.ifa_family = address_family, + .payload.ifa_index = 0}; + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR_SOCKET; + } + + n = send(fd, &msg, sizeof(msg), 0); + if (n == -1) { + goto ERR_SEND; + } + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR_RECV; + } + + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR_NL; + } + return -99; /* Unexpected */ + } + + for (; NLMSG_OK(hdr, n); hdr = NLMSG_NEXT(hdr, n)) { + struct ifaddrmsg *payload = (struct ifaddrmsg *)NLMSG_DATA(hdr); + + if (address_family == AF_INET6) { + if ((payload->ifa_index == interface_id) && + (payload->ifa_prefixlen < IPV6_ADDR_LEN * 8)) { + printf("got ip address\n"); + memcpy(ip_address->buffer, RTA_DATA(payload + 1), IPV6_ADDR_LEN); + ip_address->family = AF_INET6; + ip_address->prefix_len = IPV6_ADDR_LEN_BITS; + printf("returning %d\n", HICN_SOCKET_ERROR_NONE); + return HICN_SOCKET_ERROR_NONE; + } + } else if (address_family == AF_INET) { + if ((payload->ifa_index == interface_id) && + (payload->ifa_prefixlen < IPV4_ADDR_LEN * 8)) { + printf("got ip address\n"); + memcpy(ip_address->buffer, RTA_DATA(payload + 1), IPV4_ADDR_LEN); + ip_address->family = AF_INET; + ip_address->prefix_len = IPV4_ADDR_LEN_BITS; + printf("returning %d\n", HICN_SOCKET_ERROR_NONE); + return HICN_SOCKET_ERROR_NONE; + } + } else { + return -99; + } + } + +ERR_NL: +ERR_RECV: +ERR_SEND: +ERR_SOCKET: + printf("error getting ip address\n"); + return HICN_SOCKET_ERROR_UNSPEC; +} + +int _nl_set_ip_addr(uint32_t interface_id, ip_address_t *ip_address) { + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + size_t n; + int fd; + + struct { + struct nlmsghdr hdr; + struct ifaddrmsg payload; + } msg = { + .hdr.nlmsg_type = RTM_NEWADDR, + .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_MATCH | NLM_F_ATOMIC, + .hdr.nlmsg_seq = seq++, + .payload.ifa_family = ip_address->family, + .payload.ifa_prefixlen = ip_address->prefix_len, + .payload.ifa_flags = 0, + .payload.ifa_scope = RT_SCOPE_UNIVERSE, + .payload.ifa_index = interface_id}; + + /* Set attributes = length/type/value */ + struct rtattr ifa_address = {RTA_LENGTH(ip_address_len(ip_address)), + IFA_ADDRESS}; + // XXX maybe the reason why we have a local route ? + // struct rtattr ifa_local = { RTA_LENGTH(ip_address_len(ip_address)), + // IFA_LOCAL }; + struct iovec iov[] = { + {&msg, sizeof(msg)}, + {&ifa_address, sizeof(ifa_address)}, + {(void *)&ip_address->buffer, sizeof(ip_address->buffer)}, + // { &ifa_local, sizeof(ifa_local) }, + // { (void*)&ip_address->buffer, sizeof(ip_address->buffer) }, + }; + msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR_SOCKET; + } + + // hicn_packet_dump_iov(iov, ARRAY_SIZE(iov)); + + n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); + if (n == -1) { + goto ERR_SEND; + } + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR_RECV; + } + + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR_NL; + } + } + + return 0; + +ERR_NL: +ERR_RECV: +ERR_SEND: +ERR_SOCKET: + return -1; +} + +int _nl_up_if(uint32_t interface_id) { + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + size_t n; + int fd; + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR_SOCKET; + } + + struct { + struct nlmsghdr hdr; + struct ifinfomsg payload; + } msg = { + .hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .hdr.nlmsg_type = RTM_NEWLINK, + .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, + .payload.ifi_family = AF_UNSPEC, + .payload.ifi_index = interface_id, + .payload.ifi_flags = IFF_UP, + .payload.ifi_change = IFF_UP // 0xffffffff + }; + + n = send(fd, &msg, sizeof(msg), 0); + if (n == -1) { + goto ERR_SEND; + } + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR_RECV; + } + + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR_NL; + } + return 0; + } + +ERR_NL: +ERR_RECV: +ERR_SEND: +ERR_SOCKET: + return -1; +} + +struct route_info { + char *dst_addr; + char *src_addr; + char *gateway; + char ifName[IF_NAMESIZE]; +}; + +/* + * ip -6 route add PREFIX dev INTERFACE_NAME + */ +#if 0 +int _nl_add_in_route(const char * prefix, const uint32_t interface_id) +{ + char buffer[BUFSIZE]; + struct nlmsghdr * hdr = (struct nlmsghdr *)buffer; + size_t n; + int fd; + + int pton_fd; + unsigned char dst[sizeof(struct in6_addr)]; + char * p; + char * eptr; + char addr[strlen(prefix)]; + uint32_t dst_len; + + strncpy(addr, prefix, strlen(prefix)); + + p = strchr(addr, '/'); + if (!p) { + dst_len = IPV6_ADDR_LEN; + } else { + dst_len = strtoul(p + 1, &eptr, 10); + if (dst_len > IPV6_ADDR_LEN * 8) { + printf("E: Netmask > IPV6_ADDR_LEN"); + return -1; + } + *p = 0; + } + + pton_fd = inet_pton(AF_INET6, addr, dst); + if (pton_fd <= 0) { + if (pton_fd == 0) + ;//ERROR("Not in presentation format"); + else + perror("inet_pton"); + return -2; + } + + _nl_header(RTM_NEWROUTE, (uint8_t *)buffer, BUFSIZE, FLAGS_CREATE_MATCH); + _nl_payload_route(RT_TABLE_MAIN, dst_len, (uint8_t *)buffer, BUFSIZE); + + addAttr(hdr, BUFSIZE, RTA_DST, dst, IPV6_ADDR_LEN); + addAttr(hdr, BUFSIZE, RTA_OIF, (void*)&interface_id, sizeof(uint32_t)); + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR_SOCKET; + } + + n = send(fd, buffer, hdr->nlmsg_len, 0); + if (n == -1) { + goto ERR_SEND; + } + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR_RECV; + } + + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr * err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR_NL; + } + return 0; + } + +ERR_NL: +ERR_RECV: +ERR_SEND: +ERR_SOCKET: + return -1; + +} +#endif + +/* + * ip -6 route add local default via GATEWAY_IP table TABLE_ID + */ +int _nl_add_out_route(const char *gateway, uint8_t address_family, + const uint8_t table_id, int default_route) { + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + size_t n; + int fd; + + int pton_fd; + + if (address_family == AF_INET) { + struct in_addr gw; + + pton_fd = inet_pton(AF_INET, gateway, (struct in_addr *)&gw); + if (pton_fd < 0) { + return -1; + } + + _nl_header(RTM_NEWROUTE, (uint8_t *)buffer, BUFSIZE, + NLM_F_REQUEST | NLM_F_ACK | NLM_F_MATCH | NLM_F_ATOMIC); + _nl_payload_route(table_id, address_family, 0, (uint8_t *)buffer, BUFSIZE); + + /* gw */ + addAttr(hdr, BUFSIZE, RTA_GATEWAY, &gw, sizeof(gw)); + + } else if (address_family == AF_INET6) { + struct in6_addr gw; + + pton_fd = inet_pton(AF_INET6, gateway, (struct in6_addr *)&gw); + if (pton_fd < 0) { + return -1; + } + + _nl_header(RTM_NEWROUTE, (uint8_t *)buffer, BUFSIZE, + NLM_F_REQUEST | NLM_F_ACK | NLM_F_MATCH | NLM_F_ATOMIC); + _nl_payload_route(table_id, address_family, 0, (uint8_t *)buffer, BUFSIZE); + + /* gw */ + addAttr(hdr, BUFSIZE, RTA_GATEWAY, &gw, sizeof(gw)); + if (default_route != -1) { + addAttr(hdr, BUFSIZE, RTA_OIF, &default_route, sizeof(default_route)); + } + + } else { + return -1; + } + + // For more than 255 tables + // addAttr(msg, BUFSIZE, RTA_TABLE, &table_id, sizeof(uint32_t)); + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR_SOCKET; + } + + n = send(fd, buffer, hdr->nlmsg_len, 0); + if (n == -1) { + goto ERR_SEND; + } + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR_RECV; + } + + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR_NL; + } + return 0; + } + +ERR_NL: +ERR_RECV: +ERR_SEND: +ERR_SOCKET: + return -1; +} + +/* + * ip -6 route del local default via GATEWAY_IP table TABLE_ID + */ +int _nl_del_out_route(const char *gateway, const uint8_t address_family, + const uint8_t table_id) { + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + size_t n; + int fd; + + int pton_fd; + + if (address_family == AF_INET) { + struct in_addr gw; + + pton_fd = inet_pton(AF_INET, gateway, (struct in_addr *)&gw); + if (pton_fd < 0) { + return -1; + } + + _nl_header(RTM_DELROUTE, (uint8_t *)buffer, BUFSIZE, + NLM_F_REQUEST | NLM_F_ACK | NLM_F_MATCH | NLM_F_ATOMIC); + _nl_payload_route(table_id, address_family, 0, (uint8_t *)buffer, BUFSIZE); + + /* gw */ + addAttr(hdr, BUFSIZE, RTA_GATEWAY, &gw, sizeof(gw)); + + } else if (address_family == AF_INET6) { + struct in6_addr gw; + + pton_fd = inet_pton(AF_INET6, gateway, (struct in6_addr *)&gw); + if (pton_fd < 0) { + return -1; + } + + _nl_header(RTM_DELROUTE, (uint8_t *)buffer, BUFSIZE, + NLM_F_REQUEST | NLM_F_ACK | NLM_F_MATCH | NLM_F_ATOMIC); + _nl_payload_route(table_id, address_family, 0, (uint8_t *)buffer, BUFSIZE); + + /* gw */ + addAttr(hdr, BUFSIZE, RTA_GATEWAY, &gw, sizeof(gw)); + + } else { + return -1; + } + + // For more than 255 tables + // addAttr(msg, BUFSIZE, RTA_TABLE, &table_id, sizeof(uint32_t)); + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR_SOCKET; + } + + n = send(fd, buffer, hdr->nlmsg_len, 0); + if (n == -1) { + goto ERR_SEND; + } + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR_RECV; + } + + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR_NL; + } + return 0; + } + +ERR_NL: +ERR_RECV: +ERR_SEND: +ERR_SOCKET: + return -1; +} + +/* + * ip route del 1:2::2 dev lo table local + * + */ +int _nl_del_lo_route(const ip_address_t *ip_address) { + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + size_t n; + int fd; + + struct { + struct nlmsghdr hdr; + struct rtmsg payload; + } msg = { + .hdr.nlmsg_type = RTM_DELROUTE, + .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, + .hdr.nlmsg_seq = seq++, + .payload.rtm_family = ip_address->family, + .payload.rtm_dst_len = ip_address->prefix_len, + .payload.rtm_src_len = 0, + .payload.rtm_tos = 0, + .payload.rtm_table = RT_TABLE_LOCAL, + .payload.rtm_protocol = RTPROT_UNSPEC, + .payload.rtm_scope = RT_SCOPE_UNIVERSE, + .payload.rtm_type = RTN_UNSPEC, + .payload.rtm_flags = 0 // RTM_F_NOTIFY in 'ip route get' + }; + + /* Set attribute = length/type/value */ + uint32_t one = 1; + struct rtattr a_dst = {RTA_LENGTH(ip_address_len(ip_address)), RTA_DST}; + struct rtattr a_ifid_lo = {RTA_LENGTH(sizeof(uint32_t)), RTA_OIF}; + struct iovec iov[] = { + {&msg, sizeof(msg)}, + /* Ip address */ + {&a_dst, sizeof(a_dst)}, + {(void *)&ip_address->buffer, ip_address_len(ip_address)}, + /* Interface id */ + {&a_ifid_lo, sizeof(a_ifid_lo)}, + {&one, sizeof(one)}}; + msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR; + } + + n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); + if (n == -1) { + goto ERR; + } + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR; + } + + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR; + } + return 0; + } + + return HICN_SOCKET_ERROR_NONE; +ERR: + return HICN_SOCKET_ERROR_UNSPEC; +} + +/* + * ip -6 rule add iif INTERFACE_NAME lookup TABLE_ID + */ +int _nl_add_rule(const char *interface_name, uint8_t address_family, + const uint8_t table_id) { + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + size_t n; + int fd; + + _nl_header(RTM_NEWRULE, (uint8_t *)buffer, BUFSIZE, FLAGS_CREATE); + _nl_payload_rule(table_id, address_family, (uint8_t *)buffer, BUFSIZE); + + /* XXX iif */ + addAttr(hdr, BUFSIZE, FRA_IIFNAME, (void *)interface_name, + strlen(interface_name)); + // attr1 = addNestedAttr(hdr, IFLA_LINKINFO); + // endNestedAttr(hdr, attr1); + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR_SOCKET; + } + + n = send(fd, buffer, hdr->nlmsg_len, 0); + if (n == -1) { + goto ERR_SEND; + } + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR_RECV; + } + + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR_NL; + } + return 0; + } + +ERR_NL: +ERR_RECV: +ERR_SEND: +ERR_SOCKET: + return -1; +} + +/* + * ip -6 rule del iif INTERFACE_NAME //lookup TABLE_ID + */ +int _nl_del_rule(const char *interface_name, uint8_t address_family, + const uint8_t table_id) { + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + size_t n; + int fd; + + _nl_header(RTM_DELRULE, (uint8_t *)buffer, BUFSIZE, FLAGS_CREATE); + _nl_payload_rule(table_id, address_family, (uint8_t *)buffer, BUFSIZE); + + /* XXX iif */ + addAttr(hdr, BUFSIZE, FRA_IIFNAME, (void *)interface_name, + strlen(interface_name)); + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR_SOCKET; + } + + n = send(fd, buffer, hdr->nlmsg_len, 0); + if (n == -1) { + goto ERR_SEND; + } + + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR_RECV; + } + + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR_NL; + } + return 0; + } + +ERR_NL: +ERR_RECV: +ERR_SEND: +ERR_SOCKET: + return -1; +} + +/* + * ip -6 neigh add proxy 1:2::2 dev hicnc-cons-eth0 2>&1 | grep nei + * + */ +int _nl_add_neigh_proxy(const ip_address_t *ip_address, + const uint32_t interface_id) { + /* Buffer for holding the response, with appropriate casting on the header */ + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + + /* Used for send and receive operations on netlink socket */ + int fd; + size_t n; + + /* Packet header */ + struct { + struct nlmsghdr hdr; + struct ndmsg payload; + } msg = { + .hdr.nlmsg_type = RTM_NEWNEIGH, + .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK | NLM_F_EXCL, + .hdr.nlmsg_seq = seq++, + .payload.ndm_family = ip_address->family, + .payload.ndm_ifindex = interface_id, + .payload.ndm_state = NUD_PERMANENT, + .payload.ndm_flags = NTF_PROXY, + }; + + /* Message attributes = length/type/value */ + struct rtattr a_dst = {RTA_LENGTH(ip_address_len(ip_address)), NDA_DST}; + + /* Iovec describing the packets */ + struct iovec iov[] = { + {&msg, sizeof(msg)}, + /* Ip address */ + {&a_dst, sizeof(a_dst)}, + {(void *)&ip_address->buffer, sizeof(ip_address->buffer)}, + }; + msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); + + /* Open netlink socket */ + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR; + } + + /* Send packet */ + n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); + if (n == -1) { + goto ERR; + } + + /* Receive answer */ + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR; + } + + /* Parse answer */ + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR; + } + } + + return HICN_SOCKET_ERROR_NONE; +ERR: + return HICN_SOCKET_ERROR_UNSPEC; +} + +/* ip -6 route add 0:1::/64 dev hicn-if0 table 100 */ +/* ip -6 route add 0:2::/64 dev hicn-if1 table 100 */ +int _nl_add_in_route_table(const ip_address_t *prefix, + const uint32_t interface_id, + const uint8_t table_id) { + /* Buffer for holding the response, with appropriate casting on the header */ + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + + /* Used for send and receive operations on netlink socket */ + int fd; + size_t n; + + /* Packet header */ + struct { + struct nlmsghdr hdr; + struct rtmsg payload; + } msg = { + .hdr.nlmsg_type = RTM_NEWROUTE, + .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK | NLM_F_EXCL, + .hdr.nlmsg_seq = seq++, + .payload.rtm_family = prefix->family, + .payload.rtm_dst_len = prefix->prefix_len, // XXX ? XXX dst_len, + .payload.rtm_src_len = 0, + .payload.rtm_tos = 0, + .payload.rtm_table = table_id, /* RT_TABLE_MAIN, etc. */ + .payload.rtm_protocol = RTPROT_BOOT, + .payload.rtm_scope = + prefix->family == AF_INET6 ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK, + .payload.rtm_type = RTN_UNICAST, + .payload.rtm_flags = 0, + }; + + /* Message attributes = length/type/value */ + // XXX This could be put directly inside the iovec maybe ? XXX + struct rtattr a_dst = {RTA_LENGTH(ip_address_len(prefix)), RTA_DST}; + struct rtattr a_oif = {RTA_LENGTH(sizeof(uint32_t)), RTA_OIF}; + + /* Iovec describing the packets */ + struct iovec iov[] = { + {&msg, sizeof(msg)}, + /* Destination prefix / ip address */ + {&a_dst, sizeof(a_dst)}, + {(void *)&prefix->buffer, ip_address_len(prefix)}, + /* Output interface */ + {&a_oif, sizeof(a_oif)}, + {(void *)&interface_id, sizeof(uint32_t)}, + }; + msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); + + /* Open netlink socket */ + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR; + } + + /* Send packet */ + n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); + if (n == -1) { + goto ERR; + } + + /* Receive answer */ + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR; + } + + /* Parse answer */ + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR; + } + } + + return HICN_SOCKET_ERROR_NONE; +ERR: + return HICN_SOCKET_ERROR_UNSPEC; +} + +/* Additional helper functions */ + +int _nl_add_in_route_table_s(const char *prefix, const uint32_t interface_id, + const uint8_t table_id) { + int rc; + ip_address_t ip_address; + + rc = hicn_ip_pton(prefix, &ip_address); + if (rc < 0) { + return rc; + } + + return _nl_add_in_route_table(&ip_address, interface_id, table_id); +} + +int _nl_add_in_route_s(const char *prefix, const uint32_t interface_id) { + return _nl_add_in_route_table_s(prefix, interface_id, RT_TABLE_MAIN); +} + +////////* ip -6 rule add from all prio 10 table local */ +/* ip -6 rule add from b001::/16 prio 0 table 100 */ +int _nl_add_prio_rule(const ip_address_t *ip_address, uint8_t address_family, + const uint32_t priority, const uint8_t table_id) { + /* Buffer for holding the response, with appropriate casting on the header */ + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + + /* Used for send and receive operations on netlink socket */ + int fd; + size_t n; + + /* Packet header */ + struct { + struct nlmsghdr hdr; + struct fib_rule_hdr payload; + } msg = { + .hdr.nlmsg_type = RTM_NEWRULE, + .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK | NLM_F_EXCL, + .hdr.nlmsg_seq = seq++, + .payload.family = address_family, + //.payload.dst_len = , + .payload.src_len = ip_address ? ip_address->prefix_len : 0, + .payload.tos = 0, + .payload.table = table_id, + .payload.action = FR_ACT_TO_TBL, + .payload.flags = NLM_F_REPLACE, // 0 + }; + + /* Open netlink socket */ + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR; + } + + if (ip_address) { + /* Message attributes = length/type/value */ + struct rtattr a_src = {RTA_LENGTH(ip_address_len(ip_address)), FRA_SRC}; + struct rtattr a_prio = {RTA_LENGTH(sizeof(uint32_t)), FRA_PRIORITY}; + + /* Iovec describing the packets */ + struct iovec iov[] = { + {&msg, sizeof(msg)}, + /* Source prefix / ip_address */ + {&a_src, sizeof(a_src)}, + {(void *)&ip_address->buffer, ip_address_len(ip_address)}, + /* Priority */ + {&a_prio, sizeof(a_prio)}, + {(void *)&priority, sizeof(uint32_t)}, + }; + msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); + + /* Send packet */ + n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); + if (n == -1) { + goto ERR; + } + } else { + struct rtattr a_prio = {RTA_LENGTH(sizeof(uint32_t)), FRA_PRIORITY}; + + /* Iovec describing the packets */ + struct iovec iov[] = { + {&msg, sizeof(msg)}, + /* Priority */ + {&a_prio, sizeof(a_prio)}, + {(void *)&priority, sizeof(uint32_t)}, + }; + msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); + + /* Send packet */ + n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); + if (n == -1) { + goto ERR; + } + } + + /* Receive answer */ + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR; + } + + /* Parse answer */ + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR; + } + } + + return HICN_SOCKET_ERROR_NONE; +ERR: + return HICN_SOCKET_ERROR_UNSPEC; +} + +int _nl_add_lo_prio_rule(const ip_address_t *ip_address, uint8_t address_family, + const uint32_t priority) { + return _nl_add_prio_rule(ip_address, address_family, priority, + RT_TABLE_LOCAL); +} + +/* ip -6 rule del from all prio 0 table local */ +int _nl_del_prio_rule(const ip_address_t *ip_address, uint8_t address_family, + const uint32_t priority, const uint8_t table_id) { + /* Buffer for holding the response, with appropriate casting on the header */ + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + + /* Used for send and receive operations on netlink socket */ + int fd; + size_t n; + + /* Packet header */ + struct { + struct nlmsghdr hdr; + struct fib_rule_hdr payload; + } msg = { + .hdr.nlmsg_type = RTM_DELRULE, + .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK | NLM_F_EXCL, + .hdr.nlmsg_seq = seq++, + .payload.family = address_family, + //.payload.dst_len = , + .payload.src_len = ip_address ? ip_address->prefix_len : 0, + .payload.tos = 0, + .payload.table = table_id, + .payload.action = FR_ACT_TO_TBL, + .payload.flags = NLM_F_REPLACE, // 0 + }; + + /* Open netlink socket */ + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR; + } + + /* Message attributes = length/type/value */ + if (ip_address) { + struct rtattr a_src = {RTA_LENGTH(ip_address_len(ip_address)), FRA_SRC}; + struct rtattr a_prio = {RTA_LENGTH(sizeof(uint32_t)), FRA_PRIORITY}; + + /* Iovec describing the packets */ + struct iovec iov[] = { + {&msg, sizeof(msg)}, + /* Source prefix / ip_address */ + {&a_src, sizeof(a_src)}, + {(void *)&ip_address->buffer, ip_address_len(ip_address)}, + /* Priority */ + {&a_prio, sizeof(a_prio)}, + {(void *)&priority, sizeof(uint32_t)}, + }; + msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); + + /* Send packet */ + n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); + if (n == -1) { + goto ERR; + } + + } else { + struct rtattr a_prio = {RTA_LENGTH(sizeof(uint32_t)), FRA_PRIORITY}; + + /* Iovec describing the packets */ + struct iovec iov[] = { + {&msg, sizeof(msg)}, + /* Priority */ + {&a_prio, sizeof(a_prio)}, + {(void *)&priority, sizeof(uint32_t)}, + }; + msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); + + /* Send packet */ + n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); + if (n == -1) { + goto ERR; + } + } + + /* Receive answer */ + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR; + } + + /* Parse answer */ + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0 && + err->error != -2) { //-2 is not such file or directory + errno = -err->error; + goto ERR; + } + } + + return HICN_SOCKET_ERROR_NONE; +ERR: + return HICN_SOCKET_ERROR_UNSPEC; +} + +int _nl_del_lo_prio_rule(const ip_address_t *ip_address, uint8_t address_family, + const uint32_t priority) { + return _nl_del_prio_rule(ip_address, address_family, priority, + RT_TABLE_LOCAL); +} + +/******************************************************************************/ + +// #include +// duplicate declarations, in the meantime +#define IF_NAMESIZE 16 + +//#define WITH_TUN_PI 1 + +#ifdef WITH_TUN_PI +#define TUN_FLAGS IFF_TUN +#else +#define TUN_FLAGS IFF_TUN | IFF_NO_PI +#endif + +/* + * Taken from Kernel Documentation/networking/tuntap.txt + */ + +int tun_alloc(char *dev, int flags) { + struct ifreq ifr; + int fd, err; + char *clonedev = "/dev/net/tun"; + + /* Arguments taken by the function: + * + * char *dev: the name of an interface (or '\0'). MUST have enough + * space to hold the interface name if '\0' is passed + * int flags: interface flags (eg, IFF_TUN etc.) + */ + + /* open the clone device */ + if ((fd = open(clonedev, O_RDWR)) < 0) { + return fd; + } + + /* preparation of the struct ifr, of type "struct ifreq" */ + memset(&ifr, 0, sizeof(ifr)); + + ifr.ifr_flags = flags; + + if (*dev) { + /* if a device name was specified, put it in the structure; otherwise, + * the kernel will try to allocate the "next" device of the + * specified type */ + strncpy(ifr.ifr_name, dev, IF_NAMESIZE - 1); + } + + /* try to create the device */ + if ((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0) { + close(fd); + return err; + } + + /* if the operation was successful, write back the name of the + * interface to the variable "dev", so the caller can know + * it. Note that the caller MUST reserve space in *dev (see calling + * code below) */ + strcpy(dev, ifr.ifr_name); + + /* this is the special file descriptor that the caller will use to talk + * with the virtual interface */ + return fd; +} + +int linux_get_tun_name(const char *prefix, const char *identifier, + char *tun_name) { + snprintf(tun_name, IF_NAMESIZE, "%s-%s", prefix, + identifier ? identifier : "main"); + return 0; +} + +int linux_tun_enable_offload(int fd) { + unsigned int offload = 0, tso4 = 1, tso6 = 1, ecn = 1, ufo = 1, csum = 1; + + /* Check if our kernel supports TUNSETOFFLOAD */ + if (ioctl(fd, TUNSETOFFLOAD, 0) != 0 && errno == EINVAL) { + goto ERR_TUN; + } + + if (csum) { + offload |= TUN_F_CSUM; + if (tso4) offload |= TUN_F_TSO4; + if (tso6) offload |= TUN_F_TSO6; + if ((tso4 || tso6) && ecn) offload |= TUN_F_TSO_ECN; + if (ufo) offload |= TUN_F_UFO; + } + + if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) { + offload &= ~TUN_F_UFO; + if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) { + fprintf(stderr, "TUNSETOFFLOAD ioctl() failed: %s\n", strerror(errno)); + } + } + + return 0; + +ERR_TUN: + return -1; +} + +int linux_tun_create(char *name) { + int fd, rc; + + fd = tun_alloc(name, TUN_FLAGS); + if (fd < 0) { + // ERROR("Error connecting to tun/tap interface %s!", name); + errno = -2; + goto ERR_TUN; + } + + rc = linux_tun_enable_offload(fd); + if (rc < 0) { + // WARN("Could not enable hardware offload on TUN device"); + } else { + // INFO("Enabled hardware offload on TUN device"); + } + + return fd; + +ERR_TUN: + return -1; +} + +/* + * + * interface name can be NULL for all interfaces + */ +int linux_enable_proc(char *path) { + int ret = 0; + int fd; + + fd = open(path, O_WRONLY); + if (fd < 0) { + return -1; + } + + if (write(fd, "1", 1) != 1) { + ret = -2; + } + + close(fd); + return ret; +} + +int linux_enable_v4_forwarding() { + return linux_enable_proc("/proc/sys/net/ipv4/ip_forward"); +} + +int linux_enable_v6_forwarding(char *interface_name) { + char path[PATH_MAX]; + snprintf(path, PATH_MAX, "/proc/sys/net/ipv6/conf/%s/forwarding", + (interface_name) ? interface_name : "all"); + + return linux_enable_proc(path); +} + +int linux_enable_ndp_proxy() { + return linux_enable_proc("/proc/sys/net/ipv6/conf/all/proxy_ndp"); +} + +const hicn_socket_ops_t ops = { + .arch = "linux", + .get_tun_name = linux_get_tun_name, + .tun_create = linux_tun_create, + .enable_v4_forwarding = linux_enable_v4_forwarding, + .enable_v6_forwarding = linux_enable_v6_forwarding, + .enable_ndp_proxy = linux_enable_ndp_proxy, + .get_ifid = _nl_get_ifid, + .get_output_ifid = _nl_get_output_ifid, + .get_ip_addr = _nl_get_ip_addr, + .set_ip_addr = _nl_set_ip_addr, + .up_if = _nl_up_if, + .add_in_route_table = _nl_add_in_route_table, + .add_in_route_table_s = _nl_add_in_route_table_s, + .add_in_route_s = _nl_add_in_route_s, + .add_out_route = _nl_add_out_route, + .del_out_route = _nl_del_out_route, + .del_lo_route = _nl_del_lo_route, + .add_rule = _nl_add_rule, + .del_rule = _nl_del_rule, + .add_neigh_proxy = _nl_add_neigh_proxy, + .add_prio_rule = _nl_add_prio_rule, + .add_lo_prio_rule = _nl_add_lo_prio_rule, + .del_prio_rule = _nl_del_prio_rule, + .del_lo_prio_rule = _nl_del_lo_prio_rule, +}; diff --git a/hicn-light/src/strategies/CMakeLists.txt b/hicn-light/src/strategies/CMakeLists.txt new file mode 100755 index 000000000..7f0730b2f --- /dev/null +++ b/hicn-light/src/strategies/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/strategyImpl.h + ${CMAKE_CURRENT_SOURCE_DIR}/loadBalancer.h + ${CMAKE_CURRENT_SOURCE_DIR}/loadBalancerWithPD.h + ${CMAKE_CURRENT_SOURCE_DIR}/nexthopState.h + ${CMAKE_CURRENT_SOURCE_DIR}/nexthopStateWithPD.h + ${CMAKE_CURRENT_SOURCE_DIR}/rnd.h + ${CMAKE_CURRENT_SOURCE_DIR}/rndSegment.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/loadBalancer.c + ${CMAKE_CURRENT_SOURCE_DIR}/loadBalancerWithPD.c + ${CMAKE_CURRENT_SOURCE_DIR}/nexthopState.c + ${CMAKE_CURRENT_SOURCE_DIR}/nexthopStateWithPD.c + ${CMAKE_CURRENT_SOURCE_DIR}/rnd.c + ${CMAKE_CURRENT_SOURCE_DIR}/rndSegment.c +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) diff --git a/hicn-light/src/strategies/loadBalancer.c b/hicn-light/src/strategies/loadBalancer.c new file mode 100755 index 000000000..14e907770 --- /dev/null +++ b/hicn-light/src/strategies/loadBalancer.c @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +static void _strategyLoadBalancer_ReceiveObject(StrategyImpl *strategy, + const NumberSet *egressId, + const Message *objectMessage, + Ticks rtt); +static void _strategyLoadBalancer_OnTimeout(StrategyImpl *strategy, + const NumberSet *egressId); +static NumberSet *_strategyLoadBalancer_LookupNexthop( + StrategyImpl *strategy, const Message *interestMessage); +static NumberSet *_strategyLoadBalancer_ReturnNexthops(StrategyImpl *strategy); +static unsigned _strategyLoadBalancer_CountNexthops(StrategyImpl *strategy); +static void _strategyLoadBalancer_AddNexthop(StrategyImpl *strategy, + unsigned connectionId); +static void _strategyLoadBalancer_RemoveNexthop(StrategyImpl *strategy, + unsigned connectionId); +static void _strategyLoadBalancer_ImplDestroy(StrategyImpl **strategyPtr); +static strategy_type _strategyLoadBalancer_GetStrategy(StrategyImpl *strategy); + +static StrategyImpl _template = { + .context = NULL, + .receiveObject = &_strategyLoadBalancer_ReceiveObject, + .onTimeout = &_strategyLoadBalancer_OnTimeout, + .lookupNexthop = &_strategyLoadBalancer_LookupNexthop, + .returnNexthops = &_strategyLoadBalancer_ReturnNexthops, + .countNexthops = &_strategyLoadBalancer_CountNexthops, + .addNexthop = &_strategyLoadBalancer_AddNexthop, + .removeNexthop = &_strategyLoadBalancer_RemoveNexthop, + .destroy = &_strategyLoadBalancer_ImplDestroy, + .getStrategy = &_strategyLoadBalancer_GetStrategy, +}; + +struct strategy_load_balancer; +typedef struct strategy_load_balancer StrategyLoadBalancer; + +struct strategy_load_balancer { + double weights_sum; + // hash map from connectionId to StrategyNexthopState + PARCHashMap *strategy_state; + NumberSet *nexthops; +}; + +StrategyImpl *strategyLoadBalancer_Create() { + StrategyLoadBalancer *strategy = + parcMemory_AllocateAndClear(sizeof(StrategyLoadBalancer)); + parcAssertNotNull(strategy, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(StrategyLoadBalancer)); + + strategy->weights_sum = 0.0; + strategy->strategy_state = parcHashMap_Create(); + strategy->nexthops = numberSet_Create(); + srand(time(NULL)); + + StrategyImpl *impl = parcMemory_AllocateAndClear(sizeof(StrategyImpl)); + parcAssertNotNull(impl, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(StrategyImpl)); + memcpy(impl, &_template, sizeof(StrategyImpl)); + impl->context = strategy; + + return impl; +} + +// ======================================================= +// Dispatch API + +strategy_type _strategyLoadBalancer_GetStrategy(StrategyImpl *strategy) { + return SET_STRATEGY_LOADBALANCER; +} + +static void _update_Stats(StrategyLoadBalancer *strategy, + StrategyNexthopState *state, bool inc) { + const double ALPHA = 0.9; + double w = strategyNexthopState_GetWeight(state); + strategy->weights_sum -= w; + w = strategyNexthopState_UpdateState(state, inc, ALPHA); + strategy->weights_sum += w; +} + +static unsigned _select_Nexthop(StrategyLoadBalancer *strategy) { + double rnd = (double)rand() / (double)RAND_MAX; + double start_range = 0.0; + + PARCIterator *it = parcHashMap_CreateKeyIterator(strategy->strategy_state); + + unsigned nexthop = 100000; + while (parcIterator_HasNext(it)) { + PARCUnsigned *cid = parcIterator_Next(it); + const StrategyNexthopState *elem = + parcHashMap_Get(strategy->strategy_state, cid); + + double w = strategyNexthopState_GetWeight(elem); + + double prob = w / strategy->weights_sum; + if ((rnd >= start_range) && (rnd < (start_range + prob))) { + nexthop = parcUnsigned_GetUnsigned(cid); + break; + } else { + start_range += prob; + } + } + + parcIterator_Release(&it); + + // if no face is selected by the algorithm (for example because of a wrong + // round in the weights) we may always select the last face here. Double check + // this! + return nexthop; +} + +static void _strategyLoadBalancer_ReceiveObject(StrategyImpl *strategy, + const NumberSet *egressId, + const Message *objectMessage, + Ticks rtt) { + _strategyLoadBalancer_OnTimeout(strategy, egressId); +} + +static void _strategyLoadBalancer_OnTimeout(StrategyImpl *strategy, + const NumberSet *egressId) { + StrategyLoadBalancer *lb = (StrategyLoadBalancer *)strategy->context; + + for (unsigned i = 0; i < numberSet_Length(egressId); i++) { + unsigned outId = numberSet_GetItem(egressId, i); + PARCUnsigned *cid = parcUnsigned_Create(outId); + + const StrategyNexthopState *state = + parcHashMap_Get(lb->strategy_state, cid); + if (state != NULL) { + _update_Stats(lb, (StrategyNexthopState *)state, false); + } else { + // this may happen if we remove a face/route while downloading a file + // we should ignore this timeout + } + parcUnsigned_Release(&cid); + } +} + +static NumberSet *_strategyLoadBalancer_LookupNexthop( + StrategyImpl *strategy, const Message *interestMessage) { + StrategyLoadBalancer *lb = (StrategyLoadBalancer *)strategy->context; + + unsigned in_connection = message_GetIngressConnectionId(interestMessage); + PARCUnsigned *in = parcUnsigned_Create(in_connection); + + unsigned mapSize = parcHashMap_Size(lb->strategy_state); + NumberSet *outList = numberSet_Create(); + + if ((mapSize == 0) || + ((mapSize == 1) && parcHashMap_Contains(lb->strategy_state, in))) { + // there are no output faces or the input face is also the only output face. + // return null to avoid loops + parcUnsigned_Release(&in); + return outList; + } + + unsigned out_connection; + do { + out_connection = _select_Nexthop(lb); + } while (out_connection == in_connection); + + PARCUnsigned *out = parcUnsigned_Create(out_connection); + + const StrategyNexthopState *state = parcHashMap_Get(lb->strategy_state, out); + if (state == NULL) { + // this is an error and should not happen! + parcTrapNotImplemented( + "Try to send an interest on a face that does not exists"); + } + + _update_Stats(lb, (StrategyNexthopState *)state, true); + + parcUnsigned_Release(&in); + parcUnsigned_Release(&out); + + numberSet_Add(outList, out_connection); + return outList; +} + +static NumberSet *_strategyLoadBalancer_ReturnNexthops(StrategyImpl *strategy) { + StrategyLoadBalancer *lb = (StrategyLoadBalancer *)strategy->context; + return lb->nexthops; +} + +unsigned _strategyLoadBalancer_CountNexthops(StrategyImpl *strategy) { + StrategyLoadBalancer *lb = (StrategyLoadBalancer *)strategy->context; + return numberSet_Length(lb->nexthops); +} + +static void _strategyLoadBalancer_resetState(StrategyImpl *strategy) { + StrategyLoadBalancer *lb = (StrategyLoadBalancer *)strategy->context; + lb->weights_sum = 0.0; + PARCIterator *it = parcHashMap_CreateKeyIterator(lb->strategy_state); + + while (parcIterator_HasNext(it)) { + PARCUnsigned *cid = parcIterator_Next(it); + StrategyNexthopState *elem = + (StrategyNexthopState *)parcHashMap_Get(lb->strategy_state, cid); + + strategyNexthopState_Reset(elem); + lb->weights_sum += strategyNexthopState_GetWeight(elem); + } + + parcIterator_Release(&it); +} + +static void _strategyLoadBalancer_AddNexthop(StrategyImpl *strategy, + unsigned connectionId) { + StrategyNexthopState *state = strategyNexthopState_Create(); + + PARCUnsigned *cid = parcUnsigned_Create(connectionId); + + StrategyLoadBalancer *lb = (StrategyLoadBalancer *)strategy->context; + + if (!parcHashMap_Contains(lb->strategy_state, cid)) { + parcHashMap_Put(lb->strategy_state, cid, state); + numberSet_Add(lb->nexthops, connectionId); + _strategyLoadBalancer_resetState(strategy); + } +} + +static void _strategyLoadBalancer_RemoveNexthop(StrategyImpl *strategy, + unsigned connectionId) { + StrategyLoadBalancer *lb = (StrategyLoadBalancer *)strategy->context; + + PARCUnsigned *cid = parcUnsigned_Create(connectionId); + + if (parcHashMap_Contains(lb->strategy_state, cid)) { + parcHashMap_Remove(lb->strategy_state, cid); + numberSet_Remove(lb->nexthops, connectionId); + _strategyLoadBalancer_resetState(strategy); + } + + parcUnsigned_Release(&cid); +} + +static void _strategyLoadBalancer_ImplDestroy(StrategyImpl **strategyPtr) { + parcAssertNotNull(strategyPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*strategyPtr, + "Parameter must dereference to non-null pointer"); + + StrategyImpl *impl = *strategyPtr; + StrategyLoadBalancer *strategy = (StrategyLoadBalancer *)impl->context; + + parcHashMap_Release(&(strategy->strategy_state)); + numberSet_Release(&(strategy->nexthops)); + + parcMemory_Deallocate((void **)&strategy); + parcMemory_Deallocate((void **)&impl); + *strategyPtr = NULL; +} diff --git a/hicn-light/src/strategies/loadBalancer.h b/hicn-light/src/strategies/loadBalancer.h new file mode 100755 index 000000000..1178c30fe --- /dev/null +++ b/hicn-light/src/strategies/loadBalancer.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Forward on the less loaded path + */ + +#ifndef loadBalancer_h +#define loadBalancer_h + +#include + +StrategyImpl *strategyLoadBalancer_Create(); +#endif // loadBalancer_h diff --git a/hicn-light/src/strategies/loadBalancerWithPD.c b/hicn-light/src/strategies/loadBalancerWithPD.c new file mode 100755 index 000000000..1aad8fd89 --- /dev/null +++ b/hicn-light/src/strategies/loadBalancerWithPD.c @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +const unsigned PROBE_FREQUENCY = 1024; + +static void _strategyLoadBalancerWithPD_ReceiveObject( + StrategyImpl *strategy, const NumberSet *egressId, + const Message *objectMessage, Ticks rtt); +static void _strategyLoadBalancerWithPD_OnTimeout(StrategyImpl *strategy, + const NumberSet *egressId); +static NumberSet *_strategyLoadBalancerWithPD_LookupNexthop( + StrategyImpl *strategy, const Message *interestMessage); +static NumberSet *_strategyLoadBalancerWithPD_ReturnNexthops( + StrategyImpl *strategy); +static unsigned _strategyLoadBalancerWithPD_CountNexthops( + StrategyImpl *strategy); +static void _strategyLoadBalancerWithPD_AddNexthop(StrategyImpl *strategy, + unsigned connectionId); +static void _strategyLoadBalancerWithPD_RemoveNexthop(StrategyImpl *strategy, + unsigned connectionId); +static void _strategyLoadBalancerWithPD_ImplDestroy(StrategyImpl **strategyPtr); +static strategy_type _strategyLoadBalancerWithPD_GetStrategy( + StrategyImpl *strategy); + +static StrategyImpl _template = { + .context = NULL, + .receiveObject = &_strategyLoadBalancerWithPD_ReceiveObject, + .onTimeout = &_strategyLoadBalancerWithPD_OnTimeout, + .lookupNexthop = &_strategyLoadBalancerWithPD_LookupNexthop, + .returnNexthops = &_strategyLoadBalancerWithPD_ReturnNexthops, + .countNexthops = &_strategyLoadBalancerWithPD_CountNexthops, + .addNexthop = &_strategyLoadBalancerWithPD_AddNexthop, + .removeNexthop = &_strategyLoadBalancerWithPD_RemoveNexthop, + .destroy = &_strategyLoadBalancerWithPD_ImplDestroy, + .getStrategy = &_strategyLoadBalancerWithPD_GetStrategy, +}; + +struct strategy_load_balancer_with_pd; +typedef struct strategy_load_balancer_with_pd StrategyLoadBalancerWithPD; + +struct strategy_load_balancer_with_pd { + double weights_sum; + unsigned min_delay; + // hash map from connectionId to StrategyNexthopState + PARCHashMap *strategy_state; + NumberSet *nexthops; + ConnectionTable *connTable; + bool toInit; + unsigned int fwdPackets; +}; + +StrategyImpl *strategyLoadBalancerWithPD_Create() { + StrategyLoadBalancerWithPD *strategy = + parcMemory_AllocateAndClear(sizeof(StrategyLoadBalancerWithPD)); + parcAssertNotNull(strategy, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(StrategyLoadBalancerWithPD)); + + strategy->weights_sum = 0.0; + strategy->min_delay = INT_MAX; + strategy->strategy_state = parcHashMap_Create(); + strategy->nexthops = numberSet_Create(); + srand(time(NULL)); + + StrategyImpl *impl = parcMemory_AllocateAndClear(sizeof(StrategyImpl)); + parcAssertNotNull(impl, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(StrategyImpl)); + memcpy(impl, &_template, sizeof(StrategyImpl)); + impl->context = strategy; + strategy->connTable = NULL; + strategy->fwdPackets = 0; + strategy->toInit = true; + + return impl; +} + +void strategyLoadBalancerWithPD_SetConnectionTable(StrategyImpl *strategy, + ConnectionTable *connTable) { + StrategyLoadBalancerWithPD *lb = + (StrategyLoadBalancerWithPD *)strategy->context; + lb->connTable = connTable; +} + +// ======================================================= +// Dispatch API + +strategy_type _strategyLoadBalancerWithPD_GetStrategy(StrategyImpl *strategy) { + return SET_STRATEGY_LOADBALANCER_WITH_DELAY; +} + +static void _update_Stats(StrategyLoadBalancerWithPD *strategy, + StrategyNexthopStateWithPD *state, bool inc, + Ticks rtt) { + const double ALPHA = 0.9; + double w = strategyNexthopStateWithPD_GetWeight(state); + strategy->weights_sum -= w; + w = strategyNexthopStateWithPD_UpdateState(state, inc, strategy->min_delay, + ALPHA); + strategy->weights_sum += w; +} + +static void _sendProbes(StrategyLoadBalancerWithPD *strategy) { + unsigned size = numberSet_Length(strategy->nexthops); + for (unsigned i = 0; i < size; i++) { + unsigned nhop = numberSet_GetItem(strategy->nexthops, i); + Connection *conn = + (Connection *)connectionTable_FindById(strategy->connTable, nhop); + if (conn != NULL) { + connection_Probe(conn); + unsigned delay = connection_GetDelay(conn); + PARCUnsigned *cid = parcUnsigned_Create(nhop); + StrategyNexthopStateWithPD *elem = + (StrategyNexthopStateWithPD *)parcHashMap_Get( + strategy->strategy_state, cid); + strategyNexthopStateWithPD_SetDelay(elem, delay); + if (delay < strategy->min_delay && delay != 0) { + strategy->min_delay = delay; + } + + parcUnsigned_Release(&cid); + } + } +} + +static unsigned _select_Nexthop(StrategyLoadBalancerWithPD *strategy) { + strategy->fwdPackets++; + if (strategy->toInit || strategy->fwdPackets == PROBE_FREQUENCY) { + strategy->toInit = false; + strategy->fwdPackets = 0; + _sendProbes(strategy); + } + double rnd = (double)rand() / (double)RAND_MAX; + double start_range = 0.0; + + PARCIterator *it = parcHashMap_CreateKeyIterator(strategy->strategy_state); + + unsigned nexthop = 100000; + while (parcIterator_HasNext(it)) { + PARCUnsigned *cid = parcIterator_Next(it); + const StrategyNexthopStateWithPD *elem = + parcHashMap_Get(strategy->strategy_state, cid); + + double w = strategyNexthopStateWithPD_GetWeight(elem); + + // printf("next = %u .. pi %u avgpi %f w %f avgrtt + // %f\n",parcUnsigned_GetUnsigned(cid), + // strategyNexthopStateWithPD_GetPI(elem), + // strategyNexthopStateWithPD_GetWeight(elem), + // strategyNexthopStateWithPD_GetWeight(elem), + // strategyNexthopStateWithPD_GetAvgRTT(elem)); + + double prob = w / strategy->weights_sum; + if ((rnd >= start_range) && (rnd < (start_range + prob))) { + nexthop = parcUnsigned_GetUnsigned(cid); + break; + } else { + start_range += prob; + } + } + + parcIterator_Release(&it); + + // if no face is selected by the algorithm (for example because of a wrong + // round in the weights) we may always select the last face here. Double check + // this! + return nexthop; +} + +static void _strategyLoadBalancerWithPD_ReceiveObject( + StrategyImpl *strategy, const NumberSet *egressId, + const Message *objectMessage, Ticks rtt) { + StrategyLoadBalancerWithPD *lb = + (StrategyLoadBalancerWithPD *)strategy->context; + + for (unsigned i = 0; i < numberSet_Length(egressId); i++) { + unsigned outId = numberSet_GetItem(egressId, i); + PARCUnsigned *cid = parcUnsigned_Create(outId); + + const StrategyNexthopStateWithPD *state = + parcHashMap_Get(lb->strategy_state, cid); + if (state != NULL) { + _update_Stats(lb, (StrategyNexthopStateWithPD *)state, false, 0); + } else { + // this may happen if we remove a face/route while downloading a file + // we should ignore this timeout + } + parcUnsigned_Release(&cid); + } +} + +static void _strategyLoadBalancerWithPD_OnTimeout(StrategyImpl *strategy, + const NumberSet *egressId) { + StrategyLoadBalancerWithPD *lb = + (StrategyLoadBalancerWithPD *)strategy->context; + + for (unsigned i = 0; i < numberSet_Length(egressId); i++) { + unsigned outId = numberSet_GetItem(egressId, i); + PARCUnsigned *cid = parcUnsigned_Create(outId); + + const StrategyNexthopStateWithPD *state = + parcHashMap_Get(lb->strategy_state, cid); + if (state != NULL) { + _update_Stats(lb, (StrategyNexthopStateWithPD *)state, false, 0); + } else { + // this may happen if we remove a face/route while downloading a file + // we should ignore this timeout + } + parcUnsigned_Release(&cid); + } +} + +// ATTENTION!! This interface force us to create a NumberSet which need to be +// delited somewhere The specification in the interface requires that this +// function never returns NULL. in case we have no output face we need to return +// an empty NumberSet +static NumberSet *_strategyLoadBalancerWithPD_LookupNexthop( + StrategyImpl *strategy, const Message *interestMessage) { + StrategyLoadBalancerWithPD *lb = + (StrategyLoadBalancerWithPD *)strategy->context; + + unsigned in_connection = message_GetIngressConnectionId(interestMessage); + PARCUnsigned *in = parcUnsigned_Create(in_connection); + + unsigned mapSize = parcHashMap_Size(lb->strategy_state); + NumberSet *outList = numberSet_Create(); + + if ((mapSize == 0) || + ((mapSize == 1) && parcHashMap_Contains(lb->strategy_state, in))) { + // there are no output faces or the input face is also the only output face. + // return null to avoid loops + parcUnsigned_Release(&in); + return outList; + } + + unsigned out_connection; + do { + out_connection = _select_Nexthop(lb); + } while (out_connection == in_connection); + + PARCUnsigned *out = parcUnsigned_Create(out_connection); + + const StrategyNexthopStateWithPD *state = + parcHashMap_Get(lb->strategy_state, out); + if (state == NULL) { + // this is an error and should not happen! + parcTrapNotImplemented( + "Try to send an interest on a face that does not exists"); + } + + _update_Stats(lb, (StrategyNexthopStateWithPD *)state, true, 0); + + parcUnsigned_Release(&in); + parcUnsigned_Release(&out); + + numberSet_Add(outList, out_connection); + return outList; +} + +static NumberSet *_strategyLoadBalancerWithPD_ReturnNexthops( + StrategyImpl *strategy) { + StrategyLoadBalancerWithPD *lb = + (StrategyLoadBalancerWithPD *)strategy->context; + return lb->nexthops; +} + +unsigned _strategyLoadBalancerWithPD_CountNexthops(StrategyImpl *strategy) { + StrategyLoadBalancerWithPD *lb = + (StrategyLoadBalancerWithPD *)strategy->context; + return numberSet_Length(lb->nexthops); +} + +static void _strategyLoadBalancerWithPD_resetState(StrategyImpl *strategy) { + StrategyLoadBalancerWithPD *lb = + (StrategyLoadBalancerWithPD *)strategy->context; + lb->weights_sum = 0.0; + lb->min_delay = INT_MAX; + lb->toInit = true; + PARCIterator *it = parcHashMap_CreateKeyIterator(lb->strategy_state); + + while (parcIterator_HasNext(it)) { + PARCUnsigned *cid = parcIterator_Next(it); + StrategyNexthopStateWithPD *elem = + (StrategyNexthopStateWithPD *)parcHashMap_Get(lb->strategy_state, cid); + + strategyNexthopStateWithPD_Reset(elem); + lb->weights_sum += strategyNexthopStateWithPD_GetWeight(elem); + } + + parcIterator_Release(&it); +} + +static void _strategyLoadBalancerWithPD_AddNexthop(StrategyImpl *strategy, + unsigned connectionId) { + StrategyNexthopStateWithPD *state = strategyNexthopStateWithPD_Create(); + + PARCUnsigned *cid = parcUnsigned_Create(connectionId); + + StrategyLoadBalancerWithPD *lb = + (StrategyLoadBalancerWithPD *)strategy->context; + + if (!parcHashMap_Contains(lb->strategy_state, cid)) { + parcHashMap_Put(lb->strategy_state, cid, state); + numberSet_Add(lb->nexthops, connectionId); + _strategyLoadBalancerWithPD_resetState(strategy); + } +} + +static void _strategyLoadBalancerWithPD_RemoveNexthop(StrategyImpl *strategy, + unsigned connectionId) { + StrategyLoadBalancerWithPD *lb = + (StrategyLoadBalancerWithPD *)strategy->context; + + PARCUnsigned *cid = parcUnsigned_Create(connectionId); + + if (parcHashMap_Contains(lb->strategy_state, cid)) { + parcHashMap_Remove(lb->strategy_state, cid); + numberSet_Remove(lb->nexthops, connectionId); + _strategyLoadBalancerWithPD_resetState(strategy); + } + + parcUnsigned_Release(&cid); +} + +static void _strategyLoadBalancerWithPD_ImplDestroy( + StrategyImpl **strategyPtr) { + parcAssertNotNull(strategyPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*strategyPtr, + "Parameter must dereference to non-null pointer"); + + StrategyImpl *impl = *strategyPtr; + StrategyLoadBalancerWithPD *strategy = + (StrategyLoadBalancerWithPD *)impl->context; + + parcHashMap_Release(&(strategy->strategy_state)); + numberSet_Release(&(strategy->nexthops)); + + parcMemory_Deallocate((void **)&strategy); + parcMemory_Deallocate((void **)&impl); + *strategyPtr = NULL; +} diff --git a/hicn-light/src/strategies/loadBalancerWithPD.h b/hicn-light/src/strategies/loadBalancerWithPD.h new file mode 100755 index 000000000..6ea7f0785 --- /dev/null +++ b/hicn-light/src/strategies/loadBalancerWithPD.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Forward on the less loaded path taking into account the propagation delay of + * the first hop + */ + +#ifndef loadBalancerWithPD_h +#define loadBalancerWithPD_h + +#include +#include + +StrategyImpl *strategyLoadBalancerWithPD_Create(); +void strategyLoadBalancerWithPD_SetConnectionTable(StrategyImpl *strategy, + ConnectionTable *connTable); +#endif // loadBalancerWithPD_h diff --git a/hicn-light/src/strategies/nexthopState.c b/hicn-light/src/strategies/nexthopState.c new file mode 100755 index 000000000..ef0ffe982 --- /dev/null +++ b/hicn-light/src/strategies/nexthopState.c @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include + +#include +#include + +struct strategy_nexthop_state { + unsigned int pi; + double avg_pi; + double weight; +}; + +static bool _strategyNexthopState_Destructor( + StrategyNexthopState **instancePtr) { + return true; +} + +parcObject_ImplementAcquire(strategyNexthopState, StrategyNexthopState); + +parcObject_ImplementRelease(strategyNexthopState, StrategyNexthopState); + +parcObject_Override( + StrategyNexthopState, PARCObject, + .destructor = (PARCObjectDestructor *)_strategyNexthopState_Destructor, + .copy = (PARCObjectCopy *)strategyNexthopState_Copy, + .display = (PARCObjectDisplay *)strategyNexthopState_Display, + .toString = (PARCObjectToString *)strategyNexthopState_ToString, + .equals = (PARCObjectEquals *)strategyNexthopState_Equals, + .compare = (PARCObjectCompare *)strategyNexthopState_Compare, + .hashCode = (PARCObjectHashCode *)strategyNexthopState_HashCode, + .display = (PARCObjectDisplay *)strategyNexthopState_Display); + +void strategyNexthopState_AssertValid(const StrategyNexthopState *instance) { + parcAssertTrue(strategyNexthopState_IsValid(instance), + "StrategyNexthopState is not valid."); +} + +StrategyNexthopState *strategyNexthopState_Create() { + StrategyNexthopState *result = + parcObject_CreateInstance(StrategyNexthopState); + if (result != NULL) { + result->pi = 0; + result->avg_pi = 0.0; + result->weight = 1; + } + return result; +} + +void strategyNexthopState_Reset(StrategyNexthopState *x) { + x->pi = 0; + x->avg_pi = 0.0; + x->weight = 1; +} + +int strategyNexthopState_Compare(const StrategyNexthopState *val, + const StrategyNexthopState *other) { + if (val == NULL) { + if (other != NULL) { + return -1; + } + } else if (other == NULL) { + return 1; + } else { + strategyNexthopState_OptionalAssertValid(val); + strategyNexthopState_OptionalAssertValid(other); + + if (val->pi < other->pi) { + return -1; + } else if (val->pi > other->pi) { + return 1; + } + + if (val->avg_pi < other->avg_pi) { + return -1; + } else if (val->avg_pi > other->avg_pi) { + return 1; + } + + if (val->weight < other->weight) { + return -1; + } else if (val->weight > other->weight) { + return 1; + } + } + + return 0; +} + +StrategyNexthopState *strategyNexthopState_Copy( + const StrategyNexthopState *original) { + StrategyNexthopState *result = strategyNexthopState_Create(); + result->pi = original->pi; + result->avg_pi = original->avg_pi; + result->weight = original->weight; + + return result; +} + +void strategyNexthopState_Display(const StrategyNexthopState *instance, + int indentation) { + parcDisplayIndented_PrintLine(indentation, "StrategyNexthopState@%p {", + instance); + parcDisplayIndented_PrintLine(indentation + 1, "%d", instance->pi); + parcDisplayIndented_PrintLine(indentation + 1, "%f", instance->avg_pi); + parcDisplayIndented_PrintLine(indentation + 1, "%f", instance->weight); + parcDisplayIndented_PrintLine(indentation, "}"); +} + +bool strategyNexthopState_Equals(const StrategyNexthopState *x, + const StrategyNexthopState *y) { + bool result = false; + + if (x == y) { + result = true; + } else if (x == NULL || y == NULL) { + result = false; + } else { + strategyNexthopState_OptionalAssertValid(x); + strategyNexthopState_OptionalAssertValid(y); + + if (strategyNexthopState_Compare(x, y) == 0) { + result = true; + } + } + + return result; +} + +PARCHashCode strategyNexthopState_HashCode(const StrategyNexthopState *x) { + PARCHashCode result = 0; + char str[128]; + sprintf(str, "PI:%d: AVG_PI:%f: W:%f", x->pi, x->avg_pi, x->weight); + result = parcHashCode_Hash((uint8_t *)&str, strlen(str)); + return result; +} + +bool strategyNexthopState_IsValid(const StrategyNexthopState *x) { + bool result = false; + + if (x != NULL) { + result = true; + } + + return result; +} + +char *strategyNexthopState_ToString(const StrategyNexthopState *x) { + // this is not implemented + parcTrapNotImplemented("strategyNexthopState_ToString is not implemented"); + return NULL; +} + +unsigned strategyNexthopState_GetPI(const StrategyNexthopState *x) { + strategyNexthopState_OptionalAssertValid(x); + + return x->pi; +} + +double strategyNexthopState_GetAvgPI(const StrategyNexthopState *x) { + strategyNexthopState_OptionalAssertValid(x); + + return x->avg_pi; +} + +double strategyNexthopState_GetWeight(const StrategyNexthopState *x) { + strategyNexthopState_OptionalAssertValid(x); + + return x->weight; +} + +double strategyNexthopState_UpdateState(StrategyNexthopState *x, bool inc, + double alpha) { + if (inc) { + x->pi++; + } else { + if (x->pi > 0) { + x->pi--; + } + } + x->avg_pi = (x->avg_pi * alpha) + (x->pi * (1 - alpha)); + if (x->avg_pi == 0.0) { + x->avg_pi = 0.1; + } + x->weight = 1 / x->avg_pi; + + return x->weight; +} diff --git a/hicn-light/src/strategies/nexthopState.h b/hicn-light/src/strategies/nexthopState.h new file mode 100755 index 000000000..35a9f497b --- /dev/null +++ b/hicn-light/src/strategies/nexthopState.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef nexthopstate_h +#define nexthopstate_h + +#include +#include + +struct strategy_nexthop_state; +typedef struct strategy_nexthop_state StrategyNexthopState; +extern parcObjectDescriptor_Declaration(StrategyNexthopState); + +/** + */ +StrategyNexthopState *strategyNexthopState_Acquire( + const StrategyNexthopState *instance); + +#ifdef PARCLibrary_DISABLE_VALIDATION +#define strategyNexthopState_OptionalAssertValid(_instance_) +#else +#define strategyNexthopState_OptionalAssertValid(_instance_) \ + strategyNexthopState_AssertValid(_instance_) +#endif + +/** + */ +void strategyNexthopState_AssertValid(const StrategyNexthopState *instance); + +/** + */ +StrategyNexthopState *strategyNexthopState_Create(); + +void strategyNexthopState_Reset(StrategyNexthopState *x); +/** + */ +int strategyNexthopState_Compare(const StrategyNexthopState *instance, + const StrategyNexthopState *other); + +/** + */ +StrategyNexthopState *strategyNexthopState_Copy( + const StrategyNexthopState *original); + +/** + */ +void strategyNexthopState_Display(const StrategyNexthopState *instance, + int indentation); + +/** + */ +bool strategyNexthopState_Equals(const StrategyNexthopState *x, + const StrategyNexthopState *y); + +/** + */ +PARCHashCode strategyNexthopState_HashCode( + const StrategyNexthopState *instance); + +/** + */ +bool strategyNexthopState_IsValid(const StrategyNexthopState *instance); + +/** + */ +void strategyNexthopState_Release(StrategyNexthopState **instancePtr); + +/** + */ +char *strategyNexthopState_ToString(const StrategyNexthopState *instance); + +/** + */ +unsigned strategyNexthopState_GetPI(const StrategyNexthopState *x); + +double strategyNexthopState_GetAvgPI(const StrategyNexthopState *x); + +double strategyNexthopState_GetWeight(const StrategyNexthopState *x); + +double strategyNexthopState_UpdateState(StrategyNexthopState *x, bool inc, + double alpha); +#endif diff --git a/hicn-light/src/strategies/nexthopStateWithPD.c b/hicn-light/src/strategies/nexthopStateWithPD.c new file mode 100755 index 000000000..2eecb0c64 --- /dev/null +++ b/hicn-light/src/strategies/nexthopStateWithPD.c @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +struct strategy_nexthop_state_with_pd { + unsigned int pi; + unsigned delay; + double weight; + double avg_pi; +}; + +static bool _strategyNexthopStateWithPD_Destructor( + StrategyNexthopStateWithPD **instancePtr) { + return true; +} + +parcObject_ImplementAcquire(strategyNexthopStateWithPD, + StrategyNexthopStateWithPD); + +parcObject_ImplementRelease(strategyNexthopStateWithPD, + StrategyNexthopStateWithPD); + +parcObject_Override( + StrategyNexthopStateWithPD, PARCObject, + .destructor = (PARCObjectDestructor *) + _strategyNexthopStateWithPD_Destructor, + .copy = (PARCObjectCopy *)strategyNexthopStateWithPD_Copy, + .display = (PARCObjectDisplay *)strategyNexthopStateWithPD_Display, + .toString = (PARCObjectToString *)strategyNexthopStateWithPD_ToString, + .equals = (PARCObjectEquals *)strategyNexthopStateWithPD_Equals, + .compare = (PARCObjectCompare *)strategyNexthopStateWithPD_Compare, + .hashCode = (PARCObjectHashCode *)strategyNexthopStateWithPD_HashCode, + .display = (PARCObjectDisplay *)strategyNexthopStateWithPD_Display); + +void strategyNexthopStateWithPD_AssertValid( + const StrategyNexthopStateWithPD *instance) { + parcAssertTrue(strategyNexthopStateWithPD_IsValid(instance), + "StrategyNexthopStateWithPD is not valid."); +} + +StrategyNexthopStateWithPD *strategyNexthopStateWithPD_Create() { + StrategyNexthopStateWithPD *result = + parcObject_CreateInstance(StrategyNexthopStateWithPD); + if (result != NULL) { + result->pi = 0; + result->avg_pi = 1.0; + result->weight = 1; + result->delay = 0; + } + return result; +} + +void strategyNexthopStateWithPD_Reset(StrategyNexthopStateWithPD *x) { + x->pi = 0; + x->avg_pi = 1.0; + x->weight = 1; + x->delay = 0; +} + +int strategyNexthopStateWithPD_Compare( + const StrategyNexthopStateWithPD *val, + const StrategyNexthopStateWithPD *other) { + if (val == NULL) { + if (other != NULL) { + return -1; + } + } else if (other == NULL) { + return 1; + } else { + strategyNexthopStateWithPD_OptionalAssertValid(val); + strategyNexthopStateWithPD_OptionalAssertValid(other); + + if (val->pi < other->pi) { + return -1; + } else if (val->pi > other->pi) { + return 1; + } + + if (val->avg_pi < other->avg_pi) { + return -1; + } else if (val->avg_pi > other->avg_pi) { + return 1; + } + + if (val->weight < other->weight) { + return -1; + } else if (val->weight > other->weight) { + return 1; + } + + if (val->delay < other->delay) { + return -1; + } else if (val->delay > other->delay) { + return 1; + } + } + + return 0; +} + +StrategyNexthopStateWithPD *strategyNexthopStateWithPD_Copy( + const StrategyNexthopStateWithPD *original) { + StrategyNexthopStateWithPD *result = strategyNexthopStateWithPD_Create(); + result->pi = original->pi; + result->avg_pi = original->avg_pi; + result->weight = original->weight; + result->delay = original->delay; + + return result; +} + +void strategyNexthopStateWithPD_Display( + const StrategyNexthopStateWithPD *instance, int indentation) { + parcDisplayIndented_PrintLine(indentation, "StrategyNexthopStateWithPD@%p {", + instance); + parcDisplayIndented_PrintLine(indentation + 1, "%d", instance->pi); + parcDisplayIndented_PrintLine(indentation + 1, "%f", instance->avg_pi); + parcDisplayIndented_PrintLine(indentation + 1, "%f", instance->weight); + parcDisplayIndented_PrintLine(indentation + 1, "%f", instance->delay); + parcDisplayIndented_PrintLine(indentation, "}"); +} + +bool strategyNexthopStateWithPD_Equals(const StrategyNexthopStateWithPD *x, + const StrategyNexthopStateWithPD *y) { + bool result = false; + + if (x == y) { + result = true; + } else if (x == NULL || y == NULL) { + result = false; + } else { + strategyNexthopStateWithPD_OptionalAssertValid(x); + strategyNexthopStateWithPD_OptionalAssertValid(y); + + if (strategyNexthopStateWithPD_Compare(x, y) == 0) { + result = true; + } + } + + return result; +} + +PARCHashCode strategyNexthopStateWithPD_HashCode( + const StrategyNexthopStateWithPD *x) { + PARCHashCode result = 0; + char str[128]; + sprintf(str, "PI:%d: AVG_PI:%f: W:%f D:%d", x->pi, x->avg_pi, x->weight, + x->delay); + result = parcHashCode_Hash((uint8_t *)&str, strlen(str)); + return result; +} + +bool strategyNexthopStateWithPD_IsValid(const StrategyNexthopStateWithPD *x) { + bool result = false; + + if (x != NULL) { + result = true; + } + + return result; +} + +char *strategyNexthopStateWithPD_ToString(const StrategyNexthopStateWithPD *x) { + // this is not implemented + parcTrapNotImplemented( + "strategyNexthopStateWithPD_ToString is not implemented"); + return NULL; +} + +unsigned strategyNexthopStateWithPD_GetPI(const StrategyNexthopStateWithPD *x) { + strategyNexthopStateWithPD_OptionalAssertValid(x); + + return x->pi; +} + +double strategyNexthopStateWithPD_GetAvgPI( + const StrategyNexthopStateWithPD *x) { + strategyNexthopStateWithPD_OptionalAssertValid(x); + + return x->avg_pi; +} + +double strategyNexthopStateWithPD_GetWeight( + const StrategyNexthopStateWithPD *x) { + strategyNexthopStateWithPD_OptionalAssertValid(x); + + return x->weight; +} + +unsigned strategyNexthopStateWithPD_GetDelay( + const StrategyNexthopStateWithPD *x) { + strategyNexthopStateWithPD_OptionalAssertValid(x); + + return x->delay; +} + +void strategyNexthopStateWithPD_SetDelay(StrategyNexthopStateWithPD *x, + unsigned delay) { + strategyNexthopStateWithPD_OptionalAssertValid(x); + if (delay != 0) { + x->delay = delay; + } +} + +double strategyNexthopStateWithPD_UpdateState(StrategyNexthopStateWithPD *x, + bool inc, unsigned min_delay, + double alpha) { + strategyNexthopStateWithPD_OptionalAssertValid(x); + + if (inc) { + x->pi++; + } else { + if (x->pi > 0) { + x->pi--; + } + } + + x->avg_pi = (x->avg_pi * alpha) + (x->pi * (1 - alpha)); + if (x->avg_pi == 0.0) { + x->avg_pi = 0.1; + } + + double factor = 1.0; + if (min_delay != INT_MAX && x->delay != 0) { + factor = ((double)min_delay / (double)x->delay); + } + + x->weight = 1 / (x->avg_pi * factor); + + return x->weight; +} diff --git a/hicn-light/src/strategies/nexthopStateWithPD.h b/hicn-light/src/strategies/nexthopStateWithPD.h new file mode 100755 index 000000000..4d8bd6d15 --- /dev/null +++ b/hicn-light/src/strategies/nexthopStateWithPD.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef nexthopstatewithpd_h +#define nexthopstatewithpd_h + +#include +#include + +struct strategy_nexthop_state_with_pd; +typedef struct strategy_nexthop_state_with_pd StrategyNexthopStateWithPD; +extern parcObjectDescriptor_Declaration(StrategyNexthopStateWithPD); + +/** + */ +StrategyNexthopStateWithPD *strategyNexthopStateWithPD_Acquire( + const StrategyNexthopStateWithPD *instance); + +#ifdef PARCLibrary_DISABLE_VALIDATION +#define strategyNexthopStateWithPD_OptionalAssertValid(_instance_) +#else +#define strategyNexthopStateWithPD_OptionalAssertValid(_instance_) \ + strategyNexthopStateWithPD_AssertValid(_instance_) +#endif + +/** + */ +void strategyNexthopStateWithPD_AssertValid( + const StrategyNexthopStateWithPD *instance); + +/** + */ +StrategyNexthopStateWithPD *strategyNexthopStateWithPD_Create(); + +void strategyNexthopStateWithPD_Reset(StrategyNexthopStateWithPD *x); +/** + */ +int strategyNexthopStateWithPD_Compare( + const StrategyNexthopStateWithPD *instance, + const StrategyNexthopStateWithPD *other); + +/** + */ +StrategyNexthopStateWithPD *strategyNexthopStateWithPD_Copy( + const StrategyNexthopStateWithPD *original); + +/** + */ +void strategyNexthopStateWithPD_Display( + const StrategyNexthopStateWithPD *instance, int indentation); + +/** + */ +bool strategyNexthopStateWithPD_Equals(const StrategyNexthopStateWithPD *x, + const StrategyNexthopStateWithPD *y); + +/** + */ +PARCHashCode strategyNexthopStateWithPD_HashCode( + const StrategyNexthopStateWithPD *instance); + +/** + */ +bool strategyNexthopStateWithPD_IsValid( + const StrategyNexthopStateWithPD *instance); + +/** + */ +void strategyNexthopStateWithPD_Release( + StrategyNexthopStateWithPD **instancePtr); + +/** + */ +char *strategyNexthopStateWithPD_ToString( + const StrategyNexthopStateWithPD *instance); + +/** + */ +unsigned strategyNexthopStateWithPD_GetPI(const StrategyNexthopStateWithPD *x); + +double strategyNexthopStateWithPD_GetAvgPI(const StrategyNexthopStateWithPD *x); + +double strategyNexthopStateWithPD_GetWeight( + const StrategyNexthopStateWithPD *x); + +unsigned strategyNexthopStateWithPD_GetDelay( + const StrategyNexthopStateWithPD *x); +void strategyNexthopStateWithPD_SetDelay(StrategyNexthopStateWithPD *x, + unsigned delay); + +double strategyNexthopStateWithPD_UpdateState(StrategyNexthopStateWithPD *x, + bool inc, unsigned min_delay, + double alpha); +#endif diff --git a/hicn-light/src/strategies/rnd.c b/hicn-light/src/strategies/rnd.c new file mode 100755 index 000000000..37f3f6f30 --- /dev/null +++ b/hicn-light/src/strategies/rnd.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +static void _strategyRnd_ReceiveObject(StrategyImpl *strategy, + const NumberSet *egressId, + const Message *objectMessage, Ticks rtt); +static void _strategyRnd_OnTimeout(StrategyImpl *strategy, + const NumberSet *egressId); +static NumberSet *_strategyRnd_LookupNexthop(StrategyImpl *strategy, + const Message *interestMessage); +static NumberSet *_strategyRnd_ReturnNexthops(StrategyImpl *strategy); +static unsigned _strategyRnd_CountNexthops(StrategyImpl *strategy); +static void _strategyRnd_AddNexthop(StrategyImpl *strategy, + unsigned connectionId); +static void _strategyRnd_RemoveNexthop(StrategyImpl *strategy, + unsigned connectionId); +static void _strategyRnd_ImplDestroy(StrategyImpl **strategyPtr); +static strategy_type _strategyRnd_GetStrategy(StrategyImpl *strategy); + +static StrategyImpl _template = { + .context = NULL, + .receiveObject = &_strategyRnd_ReceiveObject, + .onTimeout = &_strategyRnd_OnTimeout, + .lookupNexthop = &_strategyRnd_LookupNexthop, + .returnNexthops = &_strategyRnd_ReturnNexthops, + .countNexthops = &_strategyRnd_CountNexthops, + .addNexthop = &_strategyRnd_AddNexthop, + .removeNexthop = &_strategyRnd_RemoveNexthop, + .destroy = &_strategyRnd_ImplDestroy, + .getStrategy = &_strategyRnd_GetStrategy, +}; + +struct strategy_rnd; +typedef struct strategy_rnd StrategyRnd; + +struct strategy_rnd { + NumberSet *nexthops; +}; + +StrategyImpl *strategyRnd_Create() { + StrategyRnd *strategy = parcMemory_AllocateAndClear(sizeof(StrategyRnd)); + parcAssertNotNull(strategy, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(StrategyRnd)); + + strategy->nexthops = numberSet_Create(); + srand(time(NULL)); + + StrategyImpl *impl = parcMemory_AllocateAndClear(sizeof(StrategyImpl)); + parcAssertNotNull(impl, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(StrategyImpl)); + memcpy(impl, &_template, sizeof(StrategyImpl)); + impl->context = strategy; + return impl; +} + +// ======================================================= +// Dispatch API + +strategy_type _strategyRnd_GetStrategy(StrategyImpl *strategy) { + return SET_STRATEGY_RANDOM; +} + +static int _select_Nexthop(StrategyRnd *strategy) { + unsigned len = numberSet_Length(strategy->nexthops); + if (len == 0) { + return -1; + } + + int rnd = (rand() % len); + return numberSet_GetItem(strategy->nexthops, rnd); +} + +static void _strategyRnd_ReceiveObject(StrategyImpl *strategy, + const NumberSet *egressId, + const Message *objectMessage, + Ticks rtt) {} + +static void _strategyRnd_OnTimeout(StrategyImpl *strategy, + const NumberSet *egressId) {} + +static NumberSet *_strategyRnd_LookupNexthop(StrategyImpl *strategy, + const Message *interestMessage) { + StrategyRnd *srnd = (StrategyRnd *)strategy->context; + + unsigned in_connection = message_GetIngressConnectionId(interestMessage); + unsigned nexthopSize = numberSet_Length(srnd->nexthops); + + NumberSet *out = numberSet_Create(); + if ((nexthopSize == 0) || + ((nexthopSize == 1) && + numberSet_Contains(srnd->nexthops, in_connection))) { + // there are no output faces or the input face is also the only output face. + // return null to avoid loops + return out; + } + + unsigned out_connection; + do { + out_connection = _select_Nexthop(srnd); + } while (out_connection == in_connection); + + if (out_connection == -1) { + return out; + } + + numberSet_Add(out, out_connection); + return out; +} + +static NumberSet *_strategyRnd_ReturnNexthops(StrategyImpl *strategy) { + StrategyRnd *srnd = (StrategyRnd *)strategy->context; + return srnd->nexthops; +} + +unsigned _strategyRnd_CountNexthops(StrategyImpl *strategy) { + StrategyRnd *srnd = (StrategyRnd *)strategy->context; + return numberSet_Length(srnd->nexthops); +} + +static void _strategyRnd_AddNexthop(StrategyImpl *strategy, + unsigned connectionId) { + StrategyRnd *srnd = (StrategyRnd *)strategy->context; + if (!numberSet_Contains(srnd->nexthops, connectionId)) { + numberSet_Add(srnd->nexthops, connectionId); + } +} + +static void _strategyRnd_RemoveNexthop(StrategyImpl *strategy, + unsigned connectionId) { + StrategyRnd *srnd = (StrategyRnd *)strategy->context; + + if (numberSet_Contains(srnd->nexthops, connectionId)) { + numberSet_Remove(srnd->nexthops, connectionId); + } +} + +static void _strategyRnd_ImplDestroy(StrategyImpl **strategyPtr) { + parcAssertNotNull(strategyPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*strategyPtr, + "Parameter must dereference to non-null pointer"); + + StrategyImpl *impl = *strategyPtr; + StrategyRnd *strategy = (StrategyRnd *)impl->context; + + numberSet_Release(&(strategy->nexthops)); + + parcMemory_Deallocate((void **)&strategy); + parcMemory_Deallocate((void **)&impl); + *strategyPtr = NULL; +} diff --git a/hicn-light/src/strategies/rnd.h b/hicn-light/src/strategies/rnd.h new file mode 100755 index 000000000..69bedc1a5 --- /dev/null +++ b/hicn-light/src/strategies/rnd.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Forward randomly + */ + +#ifndef rnd_h +#define rnd_h + +#include + +StrategyImpl* strategyRnd_Create(); +#endif // rnd_h diff --git a/hicn-light/src/strategies/rndSegment.c b/hicn-light/src/strategies/rndSegment.c new file mode 100755 index 000000000..2000ed7b7 --- /dev/null +++ b/hicn-light/src/strategies/rndSegment.c @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +static void _strategyRndSegment_ReceiveObject(StrategyImpl *strategy, + const NumberSet *egressId, + const Message *objectMessage, + Ticks rtt); +static void _strategyRndSegment_OnTimeout(StrategyImpl *strategy, + const NumberSet *egressId); +static NumberSet *_strategyRndSegment_LookupNexthop( + StrategyImpl *strategy, const Message *interestMessage); +static NumberSet *_strategyRndSegment_ReturnNexthops(StrategyImpl *strategy); +static unsigned _strategyRndSegment_CountNexthops(StrategyImpl *strategy); +static void _strategyRndSegment_AddNexthop(StrategyImpl *strategy, + unsigned connectionId); +static void _strategyRndSegment_RemoveNexthop(StrategyImpl *strategy, + unsigned connectionId); +static void _strategyRndSegment_ImplDestroy(StrategyImpl **strategyPtr); +static strategy_type _strategyRndSegment_GetStrategy(StrategyImpl *strategy); + +static StrategyImpl _template = { + .context = NULL, + .receiveObject = &_strategyRndSegment_ReceiveObject, + .onTimeout = &_strategyRndSegment_OnTimeout, + .lookupNexthop = &_strategyRndSegment_LookupNexthop, + .returnNexthops = &_strategyRndSegment_ReturnNexthops, + .countNexthops = &_strategyRndSegment_CountNexthops, + .addNexthop = &_strategyRndSegment_AddNexthop, + .removeNexthop = &_strategyRndSegment_RemoveNexthop, + .destroy = &_strategyRndSegment_ImplDestroy, + .getStrategy = &_strategyRndSegment_GetStrategy, +}; + +struct strategy_rnd_segment; +typedef struct strategy_rnd_segment StrategyRndSegment; + +struct strategy_rnd_segment { + NumberSet *nexthops; + NameBitvector *segmentName; + int last_used_face; +}; + +StrategyImpl *strategyRndSegment_Create() { + StrategyRndSegment *strategy = + parcMemory_AllocateAndClear(sizeof(StrategyRndSegment)); + parcAssertNotNull(strategy, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(StrategyRndSegment)); + + strategy->nexthops = numberSet_Create(); + strategy->segmentName = NULL; + strategy->last_used_face = 0; + srand(time(NULL)); + + StrategyImpl *impl = parcMemory_AllocateAndClear(sizeof(StrategyImpl)); + parcAssertNotNull(impl, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(StrategyImpl)); + memcpy(impl, &_template, sizeof(StrategyImpl)); + impl->context = strategy; + + return impl; +} + +// ======================================================= +// Dispatch API + +strategy_type _strategyRndSegment_GetStrategy(StrategyImpl *strategy) { + return SET_STRATEGY_RANDOM_PER_DASH_SEGMENT; +} + +static int _select_Nexthop(StrategyRndSegment *strategy) { + unsigned len = numberSet_Length(strategy->nexthops); + if (len == 0) { + return -1; + } + + int rnd = (rand() % len); + return numberSet_GetItem(strategy->nexthops, rnd); +} + +static void _strategyRndSegment_ReceiveObject(StrategyImpl *strategy, + const NumberSet *egressId, + const Message *objectMessage, + Ticks rtt) {} + +static void _strategyRndSegment_OnTimeout(StrategyImpl *strategy, + const NumberSet *egressId) {} + +static NumberSet *_strategyRndSegment_LookupNexthop( + StrategyImpl *strategy, const Message *interestMessage) { + StrategyRndSegment *srnd = (StrategyRndSegment *)strategy->context; + + unsigned in_connection = message_GetIngressConnectionId(interestMessage); + unsigned nexthopSize = numberSet_Length(srnd->nexthops); + + NumberSet *out = numberSet_Create(); + if ((nexthopSize == 0) || + ((nexthopSize == 1) && + numberSet_Contains(srnd->nexthops, in_connection))) { + // there are no output faces or the input face is also the only output face. + // return null to avoid loops + return out; + } + + NameBitvector *interestName = + name_GetContentName(message_GetName(interestMessage)); + + if (srnd->segmentName == NULL) { + srnd->segmentName = nameBitvector_Copy(interestName); + } else if (!nameBitvector_Equals(srnd->segmentName, interestName)) { + nameBitvector_Destroy(&srnd->segmentName); + srnd->segmentName = nameBitvector_Copy(interestName); + } else { + // here we need to check if the output face still exists or if someone erase + // it + if (numberSet_Contains(srnd->nexthops, srnd->last_used_face)) { + // face exists, so keep using it! + numberSet_Add(out, srnd->last_used_face); + return out; + } else { + // the face does not exists anymore, try to find a new face but keep the + // name of the dash segment + } + } + + int out_connection; + do { + out_connection = _select_Nexthop(srnd); + } while (out_connection == in_connection); + + if (out_connection == -1) { + return out; + } + + srnd->last_used_face = out_connection; + numberSet_Add(out, out_connection); + return out; +} + +static NumberSet *_strategyRndSegment_ReturnNexthops(StrategyImpl *strategy) { + StrategyRndSegment *srnd = (StrategyRndSegment *)strategy->context; + return srnd->nexthops; +} + +unsigned _strategyRndSegment_CountNexthops(StrategyImpl *strategy) { + StrategyRndSegment *srnd = (StrategyRndSegment *)strategy->context; + return numberSet_Length(srnd->nexthops); +} + +static void _strategyRndSegment_AddNexthop(StrategyImpl *strategy, + unsigned connectionId) { + StrategyRndSegment *srnd = (StrategyRndSegment *)strategy->context; + if (!numberSet_Contains(srnd->nexthops, connectionId)) { + numberSet_Add(srnd->nexthops, connectionId); + } +} + +static void _strategyRndSegment_RemoveNexthop(StrategyImpl *strategy, + unsigned connectionId) { + StrategyRndSegment *srnd = (StrategyRndSegment *)strategy->context; + + if (numberSet_Contains(srnd->nexthops, connectionId)) { + numberSet_Remove(srnd->nexthops, connectionId); + } +} + +static void _strategyRndSegment_ImplDestroy(StrategyImpl **strategyPtr) { + parcAssertNotNull(strategyPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*strategyPtr, + "Parameter must dereference to non-null pointer"); + + StrategyImpl *impl = *strategyPtr; + StrategyRndSegment *strategy = (StrategyRndSegment *)impl->context; + + numberSet_Release(&(strategy->nexthops)); + if (strategy->segmentName != NULL) { + nameBitvector_Destroy(&strategy->segmentName); + } + + parcMemory_Deallocate((void **)&strategy); + parcMemory_Deallocate((void **)&impl); + *strategyPtr = NULL; +} diff --git a/hicn-light/src/strategies/rndSegment.h b/hicn-light/src/strategies/rndSegment.h new file mode 100755 index 000000000..0749692f7 --- /dev/null +++ b/hicn-light/src/strategies/rndSegment.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Forward randomly, selects a path every time the client ask for a new dash + * segment + */ + +#ifndef rnd_Segment_h +#define rnd_Segment_h + +#include + +StrategyImpl* strategyRndSegment_Create(); +#endif // rnd_Segment_h diff --git a/hicn-light/src/strategies/strategyImpl.h b/hicn-light/src/strategies/strategyImpl.h new file mode 100755 index 000000000..c089e0b2b --- /dev/null +++ b/hicn-light/src/strategies/strategyImpl.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file strategyImpl.h + * @brief Defines the function structure for a Strategy implementation + * + * <#Detailed Description#> + * + */ + +/** + * A dispatch structure for a concrete implementation of a forwarding strategy. + */ + +#ifndef strategyImpl_h +#define strategyImpl_h + +#include +#include + +struct strategy_impl; +typedef struct strategy_impl StrategyImpl; + +/** + * @typedef StrategyImpl + * @abstract Forwarding strategy implementation + * @constant receiveObject is called when we receive an object and have a + * measured round trip time. This allows a strategy to update its performance + * data. + * @constant lookupNexthop Find the set of nexthops to use for the Interest. + * May be empty, should not be NULL. Must be destroyed. + * @constant addNexthop Add a nexthop to the list of available nexthops with a + * routing protocol-specific cost. + * @constant destroy cleans up the strategy, freeing all memory and state. A + * strategy is reference counted, so the final destruction only happens after + * the last reference is released. + * @discussion <#Discussion#> + */ +struct strategy_impl { + void *context; + void (*receiveObject)(StrategyImpl *strategy, const NumberSet *egressId, + const Message *objectMessage, Ticks rtt); + void (*onTimeout)(StrategyImpl *strategy, const NumberSet *egressId); + NumberSet *(*lookupNexthop)(StrategyImpl *strategy, + const Message *interestMessage); + NumberSet *(*returnNexthops)(StrategyImpl *strategy); + unsigned (*countNexthops)(StrategyImpl *strategy); + void (*addNexthop)(StrategyImpl *strategy, unsigned connectionId); + void (*removeNexthop)(StrategyImpl *strategy, unsigned connectionId); + void (*destroy)(StrategyImpl **strategyPtr); + strategy_type (*getStrategy)(StrategyImpl *strategy); +}; +#endif // strategyImpl_h diff --git a/hicn-light/src/utils/CMakeLists.txt b/hicn-light/src/utils/CMakeLists.txt new file mode 100755 index 000000000..7d438d157 --- /dev/null +++ b/hicn-light/src/utils/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/address.h + ${CMAKE_CURRENT_SOURCE_DIR}/addressList.h + ${CMAKE_CURRENT_SOURCE_DIR}/commands.h + ${CMAKE_CURRENT_SOURCE_DIR}/interface.h + ${CMAKE_CURRENT_SOURCE_DIR}/interfaceSet.h + ${CMAKE_CURRENT_SOURCE_DIR}/punting.h + ${CMAKE_CURRENT_SOURCE_DIR}/utils.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/address.c + ${CMAKE_CURRENT_SOURCE_DIR}/addressList.c + ${CMAKE_CURRENT_SOURCE_DIR}/interface.c + ${CMAKE_CURRENT_SOURCE_DIR}/interfaceSet.c + ${CMAKE_CURRENT_SOURCE_DIR}/punting.c + ${CMAKE_CURRENT_SOURCE_DIR}/utils.c +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/hicn-light/src/utils/address.c b/hicn-light/src/utils/address.c new file mode 100755 index 000000000..3f6fe2591 --- /dev/null +++ b/hicn-light/src/utils/address.c @@ -0,0 +1,419 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +struct address { + address_type addressType; + PARCBuffer *blob; +}; + +static struct address_type_str { + address_type type; + const char *str; +} addressTypeString[] = { + {.type = ADDR_INET, .str = "INET"}, {.type = ADDR_INET6, .str = "INET6"}, + {.type = ADDR_LINK, .str = "LINK"}, {.type = ADDR_IFACE, .str = "IFACE"}, + {.type = ADDR_UNIX, .str = "UNIX"}, {.type = 0, .str = NULL}}; + +void addressDestroy(Address **addressPtr) { + parcAssertNotNull(addressPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*addressPtr, + "Parameter must dereference to non-null pointer"); + + Address *address = *addressPtr; + parcBuffer_Release(&address->blob); + parcMemory_Deallocate((void **)&address); + *addressPtr = NULL; +} + +void addressAssertValid(const Address *address) { + parcAssertNotNull(address, "Parameter must be non-null Address *"); +} + +const char *addressTypeToString(address_type type) { + for (int i = 0; addressTypeString[i].str != NULL; i++) { + if (addressTypeString[i].type == type) { + return addressTypeString[i].str; + } + } + parcTrapIllegalValue(type, "Unknown value: %d", type); + const char *result = NULL; + return result; +} + +address_type addressStringToType(const char *str) { + for (int i = 0; addressTypeString[i].str != NULL; i++) { + if (strcasecmp(addressTypeString[i].str, str) == 0) { + return addressTypeString[i].type; + } + } + parcTrapIllegalValue(str, "Unknown type '%s'", str); + return 0; +} + +static Address *_addressCreate(address_type addressType, PARCBuffer *buffer) { + Address *result = parcMemory_AllocateAndClear(sizeof(Address)); + + parcAssertNotNull(result, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Address)); + if (result != NULL) { + result->addressType = addressType; + result->blob = buffer; + } + return result; +} + +Address *addressCreateFromInet(struct sockaddr_in *addr_in) { + parcAssertNotNull(addr_in, "Parameter must be non-null"); + + addr_in->sin_family = AF_INET; + + PARCBuffer *buffer = parcBuffer_Allocate(sizeof(struct sockaddr_in)); + parcBuffer_PutArray(buffer, sizeof(struct sockaddr_in), (uint8_t *)addr_in); + parcBuffer_Flip(buffer); + + Address *result = _addressCreate(ADDR_INET, buffer); + + return result; +} + +Address *addressCreateFromInet6(struct sockaddr_in6 *addr_in6) { + parcAssertNotNull(addr_in6, "Parameter must be non-null"); + + PARCBuffer *buffer = parcBuffer_Allocate(sizeof(struct sockaddr_in6)); + parcBuffer_PutArray(buffer, sizeof(struct sockaddr_in6), (uint8_t *)addr_in6); + parcBuffer_Flip(buffer); + + Address *result = _addressCreate(ADDR_INET6, buffer); + + return result; +} + +Address *addressCreateFromLink(const uint8_t *linkaddr, size_t length) { + parcAssertNotNull(linkaddr, "Parameter must be non-null"); + + PARCBuffer *buffer = parcBuffer_Allocate(sizeof(struct sockaddr_in6)); + parcBuffer_PutArray(buffer, length, linkaddr); + parcBuffer_Flip(buffer); + + Address *result = _addressCreate(ADDR_LINK, buffer); + return result; +} + +Address *addressCreateFromInterface(unsigned interfaceIndex) { + unsigned netbyteorder = htonl(interfaceIndex); + + PARCBuffer *buffer = parcBuffer_Allocate(sizeof(netbyteorder)); + parcBuffer_PutArray(buffer, sizeof(netbyteorder), (uint8_t *)&netbyteorder); + parcBuffer_Flip(buffer); + + Address *result = _addressCreate(ADDR_IFACE, buffer); + return result; +} + +Address *addressCreateFromUnix(struct sockaddr_un *addr_un) { + parcAssertNotNull(addr_un, "Parameter must be non-null"); + + PARCBuffer *buffer = parcBuffer_Allocate(sizeof(struct sockaddr_un)); + parcBuffer_PutArray(buffer, sizeof(struct sockaddr_un), (uint8_t *)addr_un); + parcBuffer_Flip(buffer); + + Address *result = _addressCreate(ADDR_UNIX, buffer); + return result; +} + +Address *addressCopy(const Address *original) { + addressAssertValid(original); + + Address *result = + _addressCreate(original->addressType, parcBuffer_Copy(original->blob)); + return result; +} + +bool addressEquals(const Address *a, const Address *b) { + if (a == b) { + return true; + } + + if (a == NULL || b == NULL) { + return false; + } + + if (a->addressType == b->addressType) { + if (parcBuffer_Equals(a->blob, b->blob)) { + return true; + } + } + + return false; +} + +address_type addressGetType(const Address *address) { + addressAssertValid(address); + + return address->addressType; +} + +// The Get functions need better names, what they do (Get from what? Put to +// what?) is not clear from their names. Case 1028 +bool addressGetInet(const Address *address, struct sockaddr_in *addr_in) { + addressAssertValid(address); + parcAssertNotNull(addr_in, "Parameter addr_in must be non-null"); + + if (address->addressType == ADDR_INET) { + parcAssertTrue( + parcBuffer_Remaining(address->blob) == sizeof(struct sockaddr_in), + "Address corrupted. Expected length %zu, actual length %zu", + sizeof(struct sockaddr_in), parcBuffer_Remaining(address->blob)); + + memcpy(addr_in, parcBuffer_Overlay(address->blob, 0), + sizeof(struct sockaddr_in)); + return true; + } + return false; +} + +bool addressGetInet6(const Address *address, struct sockaddr_in6 *addr_in6) { + addressAssertValid(address); + parcAssertNotNull(addr_in6, "Parameter addr_in6 must be non-null"); + + if (address->addressType == ADDR_INET6) { + parcAssertTrue( + parcBuffer_Remaining(address->blob) == sizeof(struct sockaddr_in6), + "Address corrupted. Expected length %zu, actual length %zu", + sizeof(struct sockaddr_in6), parcBuffer_Remaining(address->blob)); + + memcpy(addr_in6, parcBuffer_Overlay(address->blob, 0), + sizeof(struct sockaddr_in6)); + return true; + } + return false; +} + +bool addressGetUnix(const Address *address, struct sockaddr_un *addr_un) { + addressAssertValid(address); + parcAssertNotNull(addr_un, "Parameter addr_in6 must be non-null"); + + if (address->addressType == ADDR_UNIX) { + parcAssertTrue( + parcBuffer_Remaining(address->blob) == sizeof(struct sockaddr_un), + "Address corrupted. Expected length %zu, actual length %zu", + sizeof(struct sockaddr_un), parcBuffer_Remaining(address->blob)); + + memcpy(addr_un, parcBuffer_Overlay(address->blob, 0), + sizeof(struct sockaddr_un)); + return true; + } + return false; +} + +bool addressGetInterfaceIndex(const Address *address, uint32_t *ifidx) { + addressAssertValid(address); + parcAssertNotNull(ifidx, "Parameter ifidx must be non-null"); + + if (address->addressType == ADDR_IFACE) { + parcAssertTrue(parcBuffer_Remaining(address->blob) == sizeof(uint32_t), + "Address corrupted. Expected length %zu, actual length %zu", + sizeof(uint32_t), parcBuffer_Remaining(address->blob)); + + uint32_t netbyteorder; + memcpy(&netbyteorder, parcBuffer_Overlay(address->blob, 0), + sizeof(uint32_t)); + *ifidx = ntohl(netbyteorder); + return true; + } + return false; +} + +PARCBuffer *addressGetLinkAddress(const Address *address) { + addressAssertValid(address); + if (address->addressType == ADDR_LINK) { + return address->blob; + } + return NULL; +} + +static PARCBufferComposer *_Inet_BuildString(const Address *address, + PARCBufferComposer *composer) { + addressAssertValid(address); + + struct sockaddr_in *saddr = + (struct sockaddr_in *)parcBuffer_Overlay(address->blob, 0); + return parcNetwork_SockInet4Address_BuildString(saddr, composer); +} + +static PARCBufferComposer *_Inet6_BuildString(const Address *address, + PARCBufferComposer *composer) { + addressAssertValid(address); + + struct sockaddr_in6 *saddr = + (struct sockaddr_in6 *)parcBuffer_Overlay(address->blob, 0); + return parcNetwork_SockInet6Address_BuildString(saddr, composer); +} + +static PARCBufferComposer *_Link_BuildString(const Address *address, + PARCBufferComposer *composer) { + addressAssertValid(address); + + const unsigned char *addr = parcBuffer_Overlay(address->blob, 0); + + size_t length = parcBuffer_Remaining(address->blob); + + return parcNetwork_LinkAddress_BuildString(addr, length, composer); +} + +static ssize_t _UnixToString(char *output, size_t remaining_size, + const PARCBuffer *addr) { + parcAssertNotNull(output, "parameter output must be non-null"); + parcBuffer_AssertValid(addr); + + parcAssertTrue(parcBuffer_Remaining(addr) == sizeof(struct sockaddr_un), + "Address corrupted. Expected %zu actual %zu", + sizeof(struct sockaddr_un), parcBuffer_Remaining(addr)); + + // sockaddr length for the path, 16 for the ascii stuff, 3 for the length + // number + struct sockaddr_un *saddr = + (struct sockaddr_un *)parcBuffer_Overlay((PARCBuffer *)addr, 0); + size_t min_remaining = strlen(saddr->sun_path) + 16 + 3; + parcAssertTrue(remaining_size >= min_remaining, + "Remaining size too small, need at least %zu", min_remaining); + + ssize_t output_length = sprintf(output, "{ .path=%s, .len=%zu }", + saddr->sun_path, strlen(saddr->sun_path)); + return output_length; +} + +static ssize_t _IfaceToString(char *output, size_t remaining_size, + const PARCBuffer *addr) { + parcAssertNotNull(output, "parameter output must be non-null"); + parcBuffer_AssertValid(addr); + + parcAssertTrue(parcBuffer_Remaining(addr) == sizeof(uint32_t), + "Address corrupted. Expected %zu actual %zu", sizeof(uint32_t), + parcBuffer_Remaining(addr)); + + uint32_t *ifidx = (uint32_t *)parcBuffer_Overlay((PARCBuffer *)addr, 0); + + ssize_t output_length = sprintf(output, "{ .ifidx=%u }", ntohl(*ifidx)); + + return output_length; +} + +PARCBufferComposer *addressBuildString(const Address *address, + PARCBufferComposer *composer) { + if (address != NULL) { + char *str = addressToString(address); + parcBufferComposer_PutString(composer, str); + parcMemory_Deallocate((void **)&str); + } + return composer; +} + +char *addressToString(const Address *address) { + addressAssertValid(address); + + char addrstr[256]; + + switch (address->addressType) { + case ADDR_INET: { + PARCBufferComposer *composer = parcBufferComposer_Create(); + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer( + _Inet_BuildString(address, composer)); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + parcBufferComposer_Release(&composer); + return result; + } break; + + case ADDR_INET6: { + PARCBufferComposer *composer = parcBufferComposer_Create(); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer( + _Inet6_BuildString(address, composer)); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + parcBufferComposer_Release(&composer); + return result; + } break; + + case ADDR_LINK: + _UnixToString(addrstr, 256, address->blob); + break; + + case ADDR_IFACE: { + PARCBufferComposer *composer = parcBufferComposer_Create(); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer( + _Link_BuildString(address, composer)); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + parcBufferComposer_Release(&composer); + return result; + } break; + + case ADDR_UNIX: + _IfaceToString(addrstr, 256, address->blob); + break; + + default: + sprintf(addrstr, "UNKNOWN type = %d", address->addressType); + break; + } + + ssize_t alloc_size = 1024; + char *output = parcMemory_Allocate(alloc_size); + parcAssertNotNull(output, "parcMemory_Allocate(%zu) returned NULL", + alloc_size); + ssize_t output_length = + snprintf(output, alloc_size, "{ .type=%s, .data=%s }", + addressTypeToString(address->addressType), addrstr); + + parcAssertTrue(output_length < alloc_size, + "allocated size too small, needed %zd", output_length); + parcAssertFalse(output_length < 0, "snprintf error: (%d) %s", errno, + strerror(errno)); + + return output; +} + +PARCHashCode addressHashCode(const Address *address) { + addressAssertValid(address); + + PARCHashCode hash = parcBuffer_HashCode(address->blob); + hash = parcHashCode_HashImpl((uint8_t *)&address->addressType, + sizeof(address->addressType), hash); + + return hash; +} diff --git a/hicn-light/src/utils/address.h b/hicn-light/src/utils/address.h new file mode 100755 index 000000000..a98d15084 --- /dev/null +++ b/hicn-light/src/utils/address.h @@ -0,0 +1,498 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief Represents an endpoint address. + * + * Represents an endpoint address. May be INET, INET6, or a multi-byte LINK, + * or an Interface Index. + * + * INET and INET6 must contain the .sa_addr member, and other members as needed + * by the use of the address. + * + * The Interface Index address is essentially a pointer to a device. + * + * + * Example: + * @code + * <#example#> + * @endcode + */ +#ifndef address_h +#define address_h + +#include +#include +#include + +#include +#include +#include + +/** + * Return a string representation of the given `address_type` + * + * @param [in] type A valid address_type value. + * + * @return NULL An error occurred + * @return non-NULL A pointer to a static string representation of the + * `address_type`. + * + * Example: + * @code + * { + * const char *typeAsString = addressTypeToString(commandAddrType_INET); + * } + * @endcode + * + * @see addressStringToType + */ +const char *addressTypeToString(address_type type); + +/** + * Return a `address_type` from the given nul-terminated C string. + * + * @param [in] typeAsString A nul-terminated, C string representation of a + * `address_type`. + * + * @return A address_type + * + * Example: + * @code + * { + * address_type type = addressTypeToString("INET"); + * } + * @endcode + * + * @see addressTypeToString + */ +address_type addressStringToType(const char *typeAsString); + +struct address; +typedef struct address Address; + +/** + * Create a new `Address` instance from an IPv4 IP address, the port is + * optional. + * + * The sockaddr_in should be filled in network byte order. The newly created + * instance must eventually be destroyed by calling {@link addressDestroy}(). + * + * @param [in] addr_in The `sockaddr_in` representing the IPv4 IP address with + * which to initialize the new `Address` instance. + * @return A new instance of `Address` that must eventually be destroyed by + * calling {@link addressDestroy}(). + * + * Example: + * @code + * { + * Address *dest = addressCreateFromInet( + * &(struct sockaddr_in) { + * .sa_addr = + * inet_addr("foo.bar.com"), .sa_port = htons(9695) } ); addressDestroy(&dest); + * } + * @endcode + * @see addressDestroy + */ +Address *addressCreateFromInet(struct sockaddr_in *addr_in); + +/** + * Create a new `Address` instance from an IPv6 IP address, the port is + * optional. + * + * + * The sockaddr_in should be filled in network byte order. The newly created + * instance must eventually be destroyed by calling {@link addressDestroy}(). + * + * @param [in] addr_in6 A `sockaddr_in6` from which to initialize a new instance + * of Address + * @return A new instance of `Address` that must eventually be destroyed by + * calling {@link addressDestroy}() + * + * Example: + * @code + * { + * struct sockaddr_in6 addr_in6; + * memset(&addr_in6, 0, sizeof(struct sockaddr_in6)); + * + * inet_pton(AF_INET6, "2001:720:1500:1::a100", &(addr_in6.sin6_addr)); + * addr_in6.sin6_family = AF_INET6; + * addr_in6.sin6_port = 0x0A0B; + * addr_in6.sin6_flowinfo = 0x01020304; + * + * Address *address = addressCreateFromInet6(&addr_in6); + * + * addressDestroy(&address); + * } + * @endcode + * @see addressDestroy + */ +Address *addressCreateFromInet6(struct sockaddr_in6 *addr_in6); + +/** + * Create a new `Address` instance, initialized from a Link address. + * + * User must know the link address format (i.e. token ring vs ethernet) and have + * the address in a byte array. The array is encoded in left-to-right order. The + * newly created instance must eventually be destroyed by calling {@link + * addressDestroy}(). + * + * @param [in] linkaddr A byte array containing the link address + * @param [in] length The length of the link address byte array + * @return A new instance of `Address` that must eventually be destroyed by + * calling {@link addressDestroy}() + * + * Example: + * @code + * { + * uint8_t mac[] = { 0x14, 0x10, 0x9f, 0xd7, 0x0b, 0x89 }; + * Address *address = addressCreateFromLink(mac, sizeof(mac)); + * + * addressDestroy(&address); + * } + * @endcode + * @see addressDestroy + */ +Address *addressCreateFromLink(const uint8_t *linkaddr, size_t length); + +/** + * Create a new `Address` instance from a network interface index. + * + * The interfaceIndex should be in host byte order. The newly created instance + * must eventually be destroyed by calling {@link addressDestroy}(). + * + * @param [in] interfaceIndex The index of the interface to encode + * @return A new instance of `Address` that must eventually be destroyed by + * calling {@link addressDestroy}() + * + * Example: + * @code + * { + * Address *address = addressCreateFromInterface(2); + * + * addressDestroy(&address); + * } + * @endcode + * @see addressDestroy + */ +Address *addressCreateFromInterface(uint32_t interfaceIndex); + +/** + * Create a new Address instance from a PF_UNIX address domain. + * + * The newly created instance must eventually be destroyed by calling {@link + * addressDestroy}(). + * + * @param [in] addr_un The `struct sockaddr_un` specifying the local PF_UNIX + * socket address + * @return A new instance of `Address` that must eventually be destroyed by + * calling {@link addressDestroy}() + * + * Example: + * @code + * { + * struct sockaddr_un addr_unix; + * memset(&addr_unix, 0, sizeof(struct sockaddr_un)); + * char path[] = "/Hello/Cruel/World"; + * strcpy(addr_un.sun_path, path); + * addr_un.sun_family = AF_UNIX; + * + * Address *address = addressCreateFromUnix(&addr_un); + * + * addressDestroy(&address); + * } + * @endcode + * @see addressDestroy + */ +Address *addressCreateFromUnix(struct sockaddr_un *addr_un); + +/** + * Create a deep copy of an instance of a `Address`. A completely new, + * indedependent instance is created. + * + * The newly created instance must eventually be destroyed by calling {@link + * addressDestroy}(). + * + * @param [in] original A pointer to a `Address` instance to be copied. + * @return A new instance of a Address, deep copied from the `original` + * instance. + * + * Example: + * @code + * { + * Address *address = addressCreateFromInterface(2); + * + * Address *copy = addressCopy(address); + * + * addressDestroy(&address); + * addressDestroy(©); + * } + * @endcode + * @see addressDestroy + */ +Address *addressCopy(const Address *original); + +/** + * Deallocate an instance of a Address. + * + * The Address instance is deallocated, and any referenced data is also + * deallocated. The referenced pointer is set to NULL upon return. + * + * @param [in] addressPtr A pointer to a pointer to an instance of Address. + * + * Example: + * @code + * { + * Address *address = addressCreateFromInterface(2); + * + * addressDestroy(&address); + * } + * @endcode + */ +void addressDestroy(Address **addressPtr); + +/** + * Determine if two Address instances are equal. + * + * + * The following equivalence relations on non-null `Address` instances are + * maintained: + * + * * It is reflexive: for any non-null reference value x, `addressEquals(x, x)` + * must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `addressEquals(x, y)` must return true if and only if + * `addressEquals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `addressEquals(x, y)` returns true and + * `addressEquals(y, z)` returns true, + * then `addressEquals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `addressEquals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `addressEquals(x, NULL)` must + * return false. + * + * If one address specifies more information than other, + * e.g. a is INET with a port and b is not, they are not equal. + * + * `a` and `b` may be NULL, and NULL == NULL. + * + * @param a A pointer to a Address instance + * @param b A pointer to a Address instance + * @return true if the two instances are equal + * @return false if the two instances are not equal + * + * Example: + * @code + * { + * Address *address = addressCreateFromInterface(2); + * Address *copy = addressCopy(address); + * + * if (addressEquals(address, copy)) { + * // true + * } else { + * // false + * } + * + * addressDestroy(&address); + * addressDestroy(©); + * } + * @endcode + */ +bool addressEquals(const Address *a, const Address *b); + +/** + * Return the {@link address_type} from a specified Address. + * + * @param [in] A pointer to a Address instance + * + * @return the {@link address_type} of the specified Address instance + * + * Example: + * @code + * { + * Address *address = addressCreateFromInterface(2); + * + * address_type type = addressGetType(address); + * + * addressDestroy(&address); + * } + * @endcode + * + * @see address_type + */ +address_type addressGetType(const Address *address); + +/** + * Fills in the output parameter with an INET address. + * + * @param addr_in must be non-NULL + * @return true if INET address and output filled in, false otherwise. + * + */ +bool addressGetInet(const Address *address, struct sockaddr_in *addr_in); + +/** + * Retrieve the INET6 address associated with a `Address` instance. + * + * If the specified Address instance is of type {@link commandAddrType_INET6}, + * then populate the supplied `struct sockaddr_in6` from the Address and return + * true. If the Address is not of type `commandAddrType_INET6`, this function + * returns false. + * + * @param [in] address A pointer to a `Address` instance of type {@link + * commandAddrType_INET6}. + * @param [in] addr_in6 A pointer to a `struct sockaddr_in6`. Must be non-NULL. + * @return true If the Address instance is of type `commandAddrType_INET6` and + * `addr_in6` was filled in + * @return false If the Address instance was not of type `commandAddrType_INET6` + * or `addr_in6` could not be filled in. + * + * @see addressGetType + */ +bool addressGetInet6(const Address *address, struct sockaddr_in6 *addr_in6); + +/** + * Retrieve the interface index associated with a `Address` instance. + * + * If the specified `Address` instance is of type {@link commandAddrType_IFACE}, + * then populate the supplied `uint32_t` from the Address and return true. If + * the `Address` is not of type `commandAddrType_INET6`, this function returns + * false. + * + * @param [in] address A pointer to a `Address` instance of type {@link + * commandAddrType_IFACE}. + * @param [in] interfaceIndex A pointer to a `uint32_t` to fill in. Must be + * non-NULL. + * @return true If the Address instance is of type `commandAddrType_IFACE` and + * `interfaceIndex` was filled in. + * @return false If the Address instance was not of type `commandAddrType_IFACE` + * or `interfaceIndex` could not be filled in. + * + * @see addressGetType + */ +bool addressGetInterfaceIndex(const Address *address, uint32_t *interfaceIndex); + +/** + * Retrieve the link address associated with a `Address` instance. + * + * If the specified `Address` instance is of type {@link commandAddrType_LINK}, + * then return a pointer to the {@link PARCBuffer} containing the link address. + * If the `Address` is not of type {@link commandAddrType_LINK}, then return + * NULL. The returned PARCBuffer pointer points to memory managed by the Address + * instance, and does not need to be destroyed or released on its own. + * + * @param [in] address A pointer to a `Address` instance of type {@link + * commandAddrType_LINK}. + * @return A pointer to the {@link PARCBuffer} containing the link address. + * + * Example: + * @code + * { + * uint8_t mac[] = { 0x14, 0x10, 0x9f, 0xd7, 0x0b, 0x89 }; + * Address *address = addressCreateFromLink(mac, sizeof(mac)); + * + * PARCBuffer *macBuffer = addressGetLinkAddress(address); + * + * addressDestroy(&address); + * } + * @endcode + * @see addressGetType + */ +PARCBuffer *addressGetLinkAddress(const Address *address); + +/** + * Append the string representation of a `Address` to a specified + * `PARCBufferComposer`. + * + * @param [in] address A pointer to a `Address` instance. + * @param [in] composer A pointer to a `PARCBufferComposer` instance to which to + * append the string. + * + * @return The `PARCBufferComposer` instance that was passed in. + * + * Example: + * @code + * { + * Address *address = addressCreateFromInterface(1); + * PARCBufferComposer *composer = addressBuildString(address, + * parcBufferComposer_Create()); parcBufferComposer_Release(&composer); + * addressDestroy(&address); + * } + * @endcode + * + * @see PARCBufferComposer + */ +PARCBufferComposer *addressBuildString(const Address *address, + PARCBufferComposer *composer); + +/** + * Produce a nil-terminated string representation of the specified instance. + * + * The result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] interest A pointer to the instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, nul-terminated C string that must + * be deallocated via {@link parcMemory_Deallocate}(). + * + * Example: + * @code + * { + * Address *address = addressCreateFromInterface(1); + * + * char *string = addressToString(address); + * + * if (string != NULL) { + * printf("Address looks like: %s\n", string); + * parcMemory_Deallocate(string); + * } else { + * printf("Cannot allocate memory\n"); + * } + * + * addressDestroy(&address); + * } + * @endcode + * @see parcMemory_Deallocate + * @see addressBuildString + */ +char *addressToString(const Address *address); + +/** + * Return a non-cryptographic hash code consistent with Equals + * + * If commandAddrA == commandAddrB, then addressHashCode(commandAddrA) == + * addressHashCode(commandAddrB) + * + * @param [in] address A pointer to a Address instance. + * @return A 32-bit hashcode for the specified Address instance. + * + * Example: + * @code + * Address *address = addressCreateFromInterface(1); + * + * uint32_t hashCode = addressHashCode(address); + * + * addressDestroy(&address); + * @endcode + */ +PARCHashCode addressHashCode(const Address *address); +#endif // address_h diff --git a/hicn-light/src/utils/addressList.c b/hicn-light/src/utils/addressList.c new file mode 100755 index 000000000..4f51a11bf --- /dev/null +++ b/hicn-light/src/utils/addressList.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include + +#include +#include +#include + +struct address_list { + PARCArrayList *listOfAddress; +}; + +static void _addressListFreeAddress(void **addressVoidPtr) { + Address **addressPtr = (Address **)addressVoidPtr; + addressDestroy(addressPtr); +} + +AddressList *addressListCreate() { + AddressList *list = parcMemory_AllocateAndClear(sizeof(AddressList)); + parcAssertNotNull(list, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(AddressList)); + list->listOfAddress = parcArrayList_Create(_addressListFreeAddress); + parcAssertNotNull(list->listOfAddress, "Got null from parcArrayList_Create"); + + return list; +} + +void addressListDestroy(AddressList **addressListPtr) { + parcAssertNotNull(addressListPtr, + "Parameter must be non-null double pointer"); + parcAssertNotNull(*addressListPtr, + "Parameter must dereference to non-null pointer"); + AddressList *list = *addressListPtr; + + parcArrayList_Destroy(&list->listOfAddress); + parcMemory_Deallocate((void **)&list); + *addressListPtr = NULL; +} + +AddressList *addressListAppend(AddressList *list, Address *address) { + parcAssertNotNull(list, "Parameter list must be non-null"); + parcAssertNotNull(address, "Parameter address must be non-null"); + + parcArrayList_Add(list->listOfAddress, (PARCObject *)address); + return list; +} + +AddressList *addressListCopy(const AddressList *original) { + parcAssertNotNull(original, "Parameter must be non-null"); + + AddressList *copy = addressListCreate(); + for (int i = 0; i < parcArrayList_Size(original->listOfAddress); i++) { + Address *address = (Address *)parcArrayList_Get(original->listOfAddress, i); + parcArrayList_Add(copy->listOfAddress, (PARCObject *)addressCopy(address)); + } + + return copy; +} + +bool addressListEquals(const AddressList *a, const AddressList *b) { + parcAssertNotNull(a, "Parameter a must be non-null"); + parcAssertNotNull(b, "Parameter b must be non-null"); + + if (a == b) { + return true; + } + + if (parcArrayList_Size(a->listOfAddress) != + parcArrayList_Size(b->listOfAddress)) { + return false; + } + + for (size_t i = 0; i < parcArrayList_Size(a->listOfAddress); i++) { + const Address *addr_a = (Address *)parcArrayList_Get(a->listOfAddress, i); + const Address *addr_b = (Address *)parcArrayList_Get(b->listOfAddress, i); + if (!addressEquals(addr_a, addr_b)) { + return false; + } + } + return true; +} + +size_t addressListLength(const AddressList *list) { + parcAssertNotNull(list, "Parameter must be non-null"); + return parcArrayList_Size(list->listOfAddress); +} + +const Address *addressListGetItem(const AddressList *list, size_t item) { + parcAssertNotNull(list, "Parameter must be non-null"); + parcAssertTrue(item < addressListLength(list), + "Asked for item %zu beyond end of list %zu", item, + addressListLength(list)); + + return (Address *)parcArrayList_Get(list->listOfAddress, item); +} + +char *addressListToString(const AddressList *list) { + PARCBufferComposer *composer = parcBufferComposer_Create(); + + for (size_t i = 0; i < addressListLength(list); i++) { + char *addressString = addressToString(addressListGetItem(list, i)); + parcBufferComposer_PutString(composer, addressString); + if (i < (addressListLength(list) - 1)) { + parcBufferComposer_PutString(composer, " "); + } + parcMemory_Deallocate((void **)&addressString); + } + + PARCBuffer *buffer = parcBufferComposer_ProduceBuffer(composer); + char *result = parcBuffer_ToString(buffer); + parcBuffer_Release(&buffer); + parcBufferComposer_Release(&composer); + + return result; +} diff --git a/hicn-light/src/utils/addressList.h b/hicn-light/src/utils/addressList.h new file mode 100755 index 000000000..bcb312c14 --- /dev/null +++ b/hicn-light/src/utils/addressList.h @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief A list of Address instances. + * + * An AddressList is a list of addresses. + * It wraps a PARCLinkedList for type saftey with Address. + * + */ +#ifndef address_list_h +#define address_list_h + +#include + +struct address_list; +/** + * @typedef AddressList + * @abstract A list of Address instance pointers. + */ +typedef struct address_list AddressList; + +/** + * Create an instance of {@link AddressList} + * + * @return NULL An error occurred + * @return non-NULL A pointer to a valid AddressList instance. + * + * Example: + * @code + * { + * AddressList *list = addressListCreate(); + * + * } + * @endcode + * + * @see addressListDestroy + */ +AddressList *addressListCreate(void); + +/** + * Dellocate and destroy a AddressList instance. + * + * @param [in] addressListPtr A pointer to a pointer to a valid {@link + * AddressList}. + * + * + * Example: + * @code + * { + * AddressList *list = addressListCreate(void); + * addressListDestroy(&list); + * } + * @endcode + * + * @see addressListCreate + */ +void addressListDestroy(AddressList **addressListPtr); + +/** + * Appends the address, taking ownership of the memory + * + * @param list A pointer to a AddressList. + * @param address must be non-null + * @return The input list + * + * Example: + * @code + * <#example#> + * @endcode + */ +AddressList *addressListAppend(AddressList *list, Address *address); + +/** + * Creates a reference counted copy + * + * @param list A pointer to a valid {@link AddressList}. + * + * @return An allocated list, you must destroy it. + * + * Example: + * @code + * <#example#> + * @endcode + */ +AddressList *addressListCopy(const AddressList *list); + +/** + * Determine if two AddressList instances are equal. + * + * Two AddressList instances are equal if, and only if, they have the same + * length, with the same elements in the same order. + * + * + * The following equivalence relations on non-null `AddressList` instances are + * maintained: + * + * * It is reflexive: for any non-null reference value x, + * `AddressList_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `AddressList_Equals(x, y)` must return true if and only if + * `addressListEquals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `addressListEquals(x, y)` returns true and + * `addressListEquals(y, z)` returns true, + * then `addressListEquals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `addressListEquals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `addressListEquals(x, NULL)` must + * return false. + * + * @param a A pointer to a `AddressList` instance. + * @param b A pointer to a `AddressList` instance. + * @return true if the two `AddressList` instances are equal. + * + * Example: + * @code + * { + * AddressList *a = addressListCreate(); + * AddressList *b = addressListCreate(); + * + * if (addressListEquals(a, b)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +bool addressListEquals(const AddressList *a, const AddressList *b); + +/** + * Get the number of items in the list + * + * @param list A pointer to a {@link AddressList}. + * @return The number of items in the list. + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t addressListLength(const AddressList *list); + +/** + * Returns a const reference to an item. + * Use addressCopy if needed. + * + * Do not free or modify the returned value. + * Use addressCopy if you need a mutable instance. + * + * @param list A pointer to a AddressList. + * @param item A value less than the number of items in the given {@link + * AddressList}. + * @return Asserts if item off end of list. + * + * Example: + * @code + * <#example#> + * @endcode + */ +const Address *addressListGetItem(const AddressList *list, size_t item); + +/** + * Get a nul-terminated, C-string representation of the given {@link + * AddressList}. + * + * @param list A pointer to a valid {@link AddressList} instance. + * + * @return An allocate string representation of the {@link AddressList} that + * must be freed via `parcMemory_Deallocate()`. + * + * Example: + * @code + * <#example#> + * @endcode + */ +char *addressListToString(const AddressList *list); +#endif // address_list_h diff --git a/hicn-light/src/utils/commands.h b/hicn-light/src/utils/commands.h new file mode 100755 index 000000000..2f8ebcbfe --- /dev/null +++ b/hicn-light/src/utils/commands.h @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @file commands.h + * @brief All hicn-light commands: 14 in total. + * + * Header and payload in binary format. + */ + +#ifndef commands_h +#define commands_h + +#include +#include +#include +#include + +typedef struct in6_addr ipv6_addr_t; +typedef uint32_t ipv4_addr_t; + +union commandAddr { + ipv4_addr_t ipv4; + ipv6_addr_t ipv6; +}; + +typedef enum { + REQUEST_LIGHT = 100, + RESPONSE_LIGHT, + ACK_LIGHT, + NACK_LIGHT, + LAST_MSG_TYPE_VALUE +} message_type; + +typedef enum { + ADD_LISTENER, + ADD_CONNECTION, + LIST_CONNECTIONS, + ADD_ROUTE, + LIST_ROUTES, + REMOVE_CONNECTION, + REMOVE_ROUTE, + CACHE_STORE, + CACHE_SERVE, + CACHE_CLEAR, + SET_STRATEGY, + SET_WLDR, + ADD_PUNTING, + LIST_LISTENERS, + MAPME_ENABLE, + MAPME_DISCOVERY, + MAPME_TIMESCALE, + MAPME_RETX, + LAST_COMMAND_VALUE +} command_id; + +typedef enum { + ADDR_INET = 1, + ADDR_INET6, + ADDR_LINK, + ADDR_IFACE, + ADDR_UNIX /* PF_UNIX */ +} address_type; + +typedef enum { + UDP_CONN, + TCP_CONN, + GRE_CONN, // not implemented + HICN_CONN +} connection_type; + +typedef enum { ACTIVATE_ON, ACTIVATE_OFF } activate_type; + +//========== HEADER ========== + +typedef struct { + uint8_t messageType; + uint8_t commandID; + uint16_t length; // tells the number of structures in the payload + uint32_t seqNum; +} header_control_message; +// for the moment has to be at least 8 bytes + +// SIZE=8 + +//========== [00] ADD LISTENER ========== + +typedef enum { ETHER_MODE, IP_MODE, HICN_MODE } listener_mode; + +typedef struct { + char symbolic[16]; + // char interfaceName[16]; + union commandAddr address; + uint16_t port; + // uint16_t etherType; + uint8_t addressType; + uint8_t listenerMode; + uint8_t connectionType; +} add_listener_command; + +// SIZE=40 + +//========== [01] ADD CONNECTION ========== + +typedef struct { + char symbolic[16]; + union commandAddr remoteIp; + union commandAddr localIp; + uint16_t remotePort; + uint16_t localPort; + uint8_t ipType; + uint8_t connectionType; +} add_connection_command; + +// SIZE=56 + +//========== [02] LIST CONNECTIONS ========== + +typedef enum { + CONN_GRE, + CONN_TCP, + CONN_UDP, + CONN_MULTICAST, + CONN_L2, + CONN_HICN +} list_connections_type; + +typedef enum { + IFACE_UP = 0, + IFACE_DOWN = 1, + IFACE_UNKNOWN = 2 // not used actually +} connection_state; + +typedef struct { + add_connection_command connectionData; + uint32_t connid; + uint8_t state; +} list_connections_command; + +// SIZE=64 + +//========== [03] ADD ROUTE ========== + +typedef struct { + char symbolicOrConnid[16]; + union commandAddr address; + uint16_t cost; + uint8_t addressType; + uint8_t len; +} add_route_command; + +// SIZE=36 + +//========== [04] LIST ROUTE ========== + +typedef struct { + union commandAddr address; + uint32_t connid; + uint16_t cost; + uint8_t addressType; + uint8_t len; +} list_routes_command; + +// SIZE=24 + +//========== [05] REMOVE CONNECTION ========== + +typedef struct { + char symbolicOrConnid[16]; +} remove_connection_command; + +// SIZE=16 + +//========== [06] REMOVE ROUTE ========== + +typedef struct { + char symbolicOrConnid[16]; + union commandAddr address; + uint8_t addressType; + uint8_t len; +} remove_route_command; + +// SIZE=36 + +//========== [07] CACHE STORE ========== + +typedef struct { + uint8_t activate; +} cache_store_command; + +// SIZE=1 + +//========== [08] CACHE SERVE ========== + +typedef struct { + uint8_t activate; +} cache_serve_command; + +// SIZE=1 + +//========== [09] SET STRATEGY ========== + +typedef enum { + SET_STRATEGY_LOADBALANCER, + SET_STRATEGY_RANDOM, + SET_STRATEGY_RANDOM_PER_DASH_SEGMENT, + SET_STRATEGY_LOADBALANCER_WITH_DELAY, + SET_STRATEGY_LOADBALANCER_BY_RATE, + SET_STRATEGY_LOADBALANCER_BEST_ROUTE, + LAST_STRATEGY_VALUE +} strategy_type; + +typedef struct { + union commandAddr address; + uint8_t strategyType; + uint8_t addressType; + uint8_t len; +} set_strategy_command; + +// SIZE=20 + +//========== [11] SET WLDR ========== + +typedef struct { + char symbolicOrConnid[16]; + uint8_t activate; +} set_wldr_command; + +// SIZE=17 + +//========== [12] ADD PUNTING ========== + +typedef struct { + char symbolicOrConnid[16]; + union commandAddr address; + uint8_t addressType; + uint8_t len; +} add_punting_command; + +// SIZE=36 + +//========== [13] LIST LISTENER ========== + +typedef struct { + union commandAddr address; + uint32_t connid; + uint16_t port; + uint8_t addressType; + uint8_t encapType; +} list_listeners_command; + +// SIZE=24 + +//========== [14] MAPME ========== + +// (enable/discovery/timescale/retx) + +typedef struct { + uint8_t activate; +} mapme_activator_command; + +// SIZE=1 + +typedef struct { + uint32_t timePeriod; +} mapme_timing_command; + +// SIZE=1 + +#endif diff --git a/hicn-light/src/utils/interface.c b/hicn-light/src/utils/interface.c new file mode 100755 index 000000000..ab7a88f0f --- /dev/null +++ b/hicn-light/src/utils/interface.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +struct interface { + char *name; + unsigned interfaceIndex; + bool loopback; + bool supportMulticast; + unsigned mtu; + + AddressList *addressList; +}; + +char *interfaceToString(const Interface *interface) { + PARCBufferComposer *composer = parcBufferComposer_Create(); + + parcBufferComposer_Format( + composer, "%3u %10s %1s%1s %8u ", interface->interfaceIndex, + interface->name, interface->loopback ? "l" : " ", + interface->supportMulticast ? "m" : " ", interface->mtu); + + for (size_t i = 0; i < addressListLength(interface->addressList); i++) { + addressBuildString(addressListGetItem(interface->addressList, i), composer); + if (i < (addressListLength(interface->addressList) - 1)) { + parcBufferComposer_PutStrings(composer, "\n", NULL); + } + } + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + parcBufferComposer_Release(&composer); + return result; +} + +Interface *interfaceCreate(const char *name, unsigned interfaceIndex, + bool loopback, bool supportMulticast, unsigned mtu) { + Interface *iface = parcMemory_AllocateAndClear(sizeof(Interface)); + + parcAssertNotNull(iface, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Interface)); + iface->name = parcMemory_StringDuplicate(name, 64); + iface->interfaceIndex = interfaceIndex; + iface->loopback = loopback; + iface->supportMulticast = supportMulticast; + iface->mtu = mtu; + iface->addressList = addressListCreate(); + + return iface; +} + +void interfaceDestroy(Interface **interfacePtr) { + parcAssertNotNull(interfacePtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*interfacePtr, + "Parameter must dereference to non-null pointer"); + + Interface *iface = *interfacePtr; + parcMemory_Deallocate((void **)&iface->name); + addressListDestroy(&iface->addressList); + parcMemory_Deallocate((void **)&iface); + interfacePtr = NULL; +} + +void interfaceAddAddress(Interface *iface, Address *address) { + parcAssertNotNull(iface, "Parameter iface must be non-null"); + + size_t length = addressListLength(iface->addressList); + for (size_t i = 0; i < length; i++) { + const Address *a = addressListGetItem(iface->addressList, i); + if (addressEquals(a, address)) { + return; + } + } + + addressListAppend(iface->addressList, address); +} + +const AddressList *interfaceGetAddresses(const Interface *iface) { + parcAssertNotNull(iface, "Parameter iface must be non-null"); + return iface->addressList; +} + +unsigned interfaceGetInterfaceIndex(const Interface *iface) { + parcAssertNotNull(iface, "Parameter iface must be non-null"); + return iface->interfaceIndex; +} + +bool interfaceNameEquals(const Interface *iface, const char *name) { + parcAssertNotNull(iface, "Parameter iface must be non-null"); + + if (strcasecmp(iface->name, name) == 0) { + return true; + } + return false; +} + +bool interfaceEquals(const Interface *a, const Interface *b) { + if (a == NULL && b == NULL) { + return true; + } + + if (a == NULL || b == NULL) { + return false; + } + + if (a->interfaceIndex == b->interfaceIndex) { + if (a->loopback == b->loopback) { + if (a->supportMulticast == b->supportMulticast) { + if (a->mtu == b->mtu) { + if (strcasecmp(a->name, b->name) == 0) { + if (addressListEquals(a->addressList, b->addressList)) { + return true; + } + } + } + } + } + } + return false; +} + +// static const char light_Iface[] = "Interface"; +// static const char light_IfName[] = "Name"; +// static const char light_IFIDX[] = "Index"; +// static const char light_IsLoopback[] = "Loopback"; +// static const char light_Multicast[] = "Multicast"; +// static const char light_MTU[] = "MTU"; + +// static const char light_True[] = "true"; +// static const char light_False[] = "false"; +// static const char light_Addrs[] = "Addrs"; + +const char *interfaceGetName(const Interface *iface) { + parcAssertNotNull(iface, "Parameter iface must be non-null"); + return iface->name; +} + +unsigned interfaceGetMTU(const Interface *iface) { + parcAssertNotNull(iface, "Parameter iface must be non-null"); + return iface->mtu; +} diff --git a/hicn-light/src/utils/interface.h b/hicn-light/src/utils/interface.h new file mode 100755 index 000000000..0810ec053 --- /dev/null +++ b/hicn-light/src/utils/interface.h @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef interface_h +#define interface_h + +#include +#include + +struct interface; +typedef struct interface Interface; + +/** + * Creates a representation of an interface + * + * The name is copied. Creates a representation of a system interface. + * + * @param <#param1#> + * @return An allocated object, you must call interfaceDestroy() + * + * Example: + * @code + * <#example#> + * @endcode + */ +Interface *interfaceCreate(const char *name, unsigned interfaceIndex, + bool loopback, bool supportMulticast, unsigned mtu); + +void interfaceDestroy(Interface **interfacePtr); + +/** + * Adds an address to an interface + * + * Does not allow duplicates, if already exists is not added again + * + * @param <#param1#> + * @return <#return#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void interfaceAddAddress(Interface *iface, Address *address); + +/** + * Retrieves a list of interface addresses + * + * <#Discussion#> + * + * @param <#param1#> + * @return Will not be NULL, but may be empty + * + * Example: + * @code + * <#example#> + * @endcode + */ +const AddressList *interfaceGetAddresses(const Interface *iface); + +/** + * The interface index + * + * <#Discussion#> + * + * @param <#param1#> + * @return <#return#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +unsigned interfaceGetInterfaceIndex(const Interface *iface); + +/** + * Returns the interface name, e.g. "eth0" + * + * <#Paragraphs Of Explanation#> + * + * @param [in] iface An allocated Interface + * + * @return non-null The interface Name as a C-string + * @return null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +const char *interfaceGetName(const Interface *iface); + +/** + * Returns the Maximum Transmission Unit (MTU) of the interface + * + * <#Paragraphs Of Explanation#> + * + * @param [in] iface An allocated Interface + * + * @return number The MTU as reported by the kernel + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +unsigned interfaceGetMTU(const Interface *iface); + +/** + * Determine if two InterfaceName instances are equal. + * + * + * The following equivalence relations on non-null `InterfaceName` instances are + * maintained: + * + * * It is reflexive: for any non-null reference value x, + * `InterfaceName_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `InterfaceName_Equals(x, y)` must return true if and only if + * `InterfaceName_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `InterfaceName_Equals(x, y)` returns true and + * `InterfaceName_Equals(y, z)` returns true, + * then `InterfaceName_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `InterfaceName_Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `InterfaceName_Equals(x, NULL)` must + * return false. + * + * @param a A pointer to a `InterfaceName` instance. + * @param b A pointer to a `InterfaceName` instance. + * @return true if the two `InterfaceName` instances are equal. + * + * Example: + * @code + * { + * InterfaceName *a = InterfaceName_Create(); + * InterfaceName *b = InterfaceName_Create(); + * + * if (InterfaceName_Equals(a, b)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +bool interfaceNameEquals(const Interface *iface, const char *name); + +/** + * Two Interfaces are idential + * + * All properties must be the same. The order of addresses matters, and + * they must have been added to the address list in the same order. + * + * The interface name match is case in-sensitive. + * + * @param <#param1#> + * @return <#return#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool interfaceEquals(const Interface *a, const Interface *b); + +/** + * <#OneLineDescription#> + * + * <#Discussion#> + * + * @param interface A Interface structure pointer. + * @return An allocate string representation of the Interface that must be freed + * via parcMemory_Deallocate(). + * + * Example: + * @code + * <#example#> + * @endcode + */ +char *interfaceToString(const Interface *interface); +#endif // interface_h diff --git a/hicn-light/src/utils/interfaceSet.c b/hicn-light/src/utils/interfaceSet.c new file mode 100755 index 000000000..3f56ec167 --- /dev/null +++ b/hicn-light/src/utils/interfaceSet.c @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include +#include + +#include + +struct interfaceSet { + PARCArrayList *listOfInterfaces; +}; + +static void _destroyInterface(void **ifaceVoidPtr) { + interfaceDestroy((Interface **)ifaceVoidPtr); +} + +InterfaceSet *interfaceSetCreate(void) { + InterfaceSet *set = parcMemory_AllocateAndClear(sizeof(InterfaceSet)); + parcAssertNotNull(set, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(InterfaceSet)); + set->listOfInterfaces = parcArrayList_Create(_destroyInterface); + return set; +} + +void interfaceSetDestroy(InterfaceSet **setPtr) { + parcAssertNotNull(setPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*setPtr, "Parameter must dereference to non-null pointer"); + + InterfaceSet *set = *setPtr; + parcArrayList_Destroy(&set->listOfInterfaces); + parcMemory_Deallocate((void **)&set); + *setPtr = NULL; +} + +bool interfaceSetAdd(InterfaceSet *set, Interface *iface) { + parcAssertNotNull(set, "Parameter set must be non-null"); + parcAssertNotNull(iface, "Parameter iface must be non-null"); + + unsigned ifaceIndex = interfaceGetInterfaceIndex(iface); + size_t length = parcArrayList_Size(set->listOfInterfaces); + for (size_t i = 0; i < length; i++) { + Interface *listEntry = + (Interface *)parcArrayList_Get(set->listOfInterfaces, i); + unsigned entryInterfaceIndex = interfaceGetInterfaceIndex(listEntry); + if (entryInterfaceIndex == ifaceIndex) { + return false; + } + } + + parcArrayList_Add(set->listOfInterfaces, (PARCObject *)iface); + return true; +} + +size_t interfaceSetLength(const InterfaceSet *set) { + parcAssertNotNull(set, "Parameter set must be non-null"); + return parcArrayList_Size(set->listOfInterfaces); +} + +Interface *interfaceSetGetByOrdinalIndex(InterfaceSet *set, + size_t ordinalIndex) { + parcAssertNotNull(set, "Parameter set must be non-null"); + return (Interface *)parcArrayList_Get(set->listOfInterfaces, ordinalIndex); +} + +Interface *interfaceSetGetByInterfaceIndex(const InterfaceSet *set, + unsigned interfaceIndex) { + size_t length = parcArrayList_Size(set->listOfInterfaces); + for (size_t i = 0; i < length; i++) { + Interface *listEntry = + (Interface *)parcArrayList_Get(set->listOfInterfaces, i); + unsigned entryInterfaceIndex = interfaceGetInterfaceIndex(listEntry); + if (entryInterfaceIndex == interfaceIndex) { + return listEntry; + } + } + return NULL; +} + +/** + * Uses the system name (e.g. "en0") + * + * <#Discussion#> + * + * @param <#param1#> + * @return NULL if not found + * + * Example: + * @code + * <#example#> + * @endcode + */ +Interface *interfaceSetGetByName(InterfaceSet *set, const char *name) { + size_t length = parcArrayList_Size(set->listOfInterfaces); + for (size_t i = 0; i < length; i++) { + Interface *listEntry = + (Interface *)parcArrayList_Get(set->listOfInterfaces, i); + if (interfaceNameEquals(listEntry, name)) { + return listEntry; + } + } + return NULL; +} + +bool interfaceSetEquals(const InterfaceSet *a, const InterfaceSet *b) { + if (a == NULL && b == NULL) { + return true; + } + + if (a == NULL || b == NULL) { + return false; + } + + size_t length_a = parcArrayList_Size(a->listOfInterfaces); + size_t length_b = parcArrayList_Size(b->listOfInterfaces); + + if (length_a == length_b) { + for (size_t i = 0; i < length_a; i++) { + Interface *iface_a = + (Interface *)parcArrayList_Get(a->listOfInterfaces, i); + + // the set is unique by interface id, so if it exists in set b, it + // exists there by interface id + Interface *iface_b = interfaceSetGetByInterfaceIndex( + b, interfaceGetInterfaceIndex(iface_a)); + if (!interfaceEquals(iface_b, iface_b)) { + return false; + } + } + return true; + } + return false; +} diff --git a/hicn-light/src/utils/interfaceSet.h b/hicn-light/src/utils/interfaceSet.h new file mode 100755 index 000000000..8eb8397fb --- /dev/null +++ b/hicn-light/src/utils/interfaceSet.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef InterfaceSet_h +#define InterfaceSet_h + +#include + +struct interfaceSet; +/** + * + * @see interfaceSetCreate + */ +typedef struct interfaceSet InterfaceSet; + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +InterfaceSet *interfaceSetCreate(void); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +void interfaceSetDestroy(InterfaceSet **setPtr); + +/** + * Adds interface to set, does not allow duplicates + * + * Takes ownership of the iface memory if added + * + * Duplicates are two entries with the same interface index + * + * @param <#param1#> + * @return true if added, false if not (likely a duplicate) + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool interfaceSetAdd(InterfaceSet *set, Interface *iface); + +/** + * The number of interfaces in the set + * + * <#Discussion#> + * + * @param <#param1#> + * @return <#return#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t interfaceSetLength(const InterfaceSet *set); + +/** + * Uses the ordinal index of the interface in the Set + * + * Ranges from 0 .. interfaceSetLength()-1. + * + * @param <#param1#> + * @return NULL if not found + * + * Example: + * @code + * <#example#> + * @endcode + */ +Interface *interfaceSetGetByOrdinalIndex(InterfaceSet *set, + size_t ordinalIndex); + +/** + * Retreives by the assigned interface index + * + * <#Discussion#> + * + * @param <#param1#> + * @return NULL if not found + * + * Example: + * @code + * <#example#> + * @endcode + */ +Interface *interfaceSetGetByInterfaceIndex(const InterfaceSet *set, + unsigned interfaceIndex); + +/** + * Uses the system name (e.g. "en0") + * + * <#Discussion#> + * + * @param <#param1#> + * @return NULL if not found + * + * Example: + * @code + * <#example#> + * @endcode + */ +Interface *interfaceSetGetByName(InterfaceSet *set, const char *name); + +/** + * Determine if two InterfaceSet instances are equal. + * + * Two InterfaceSet instances are equal if, and only if, the sets contain the + * same elements + * - order independent. + * Each element is compared via interfaceEquals() + * + * The following equivalence relations on non-null `InterfaceSet` instances are + * maintained: + * + * * It is reflexive: for any non-null reference value x, + * `InterfaceSet_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `InterfaceSet_Equals(x, y)` must return true if and only if + * `interfaceSetEquals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `interfaceSetEquals(x, y)` returns true and + * `interfaceSetEquals(y, z)` returns true, + * then `interfaceSetEquals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `interfaceSetEquals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `interfaceSetEquals(x, NULL)` must + * return false. + * + * @param a A pointer to a `InterfaceSet` instance. + * @param b A pointer to a `InterfaceSet` instance. + * @return true if the two `InterfaceSet` instances are equal. + * + * Example: + * @code + * { + * InterfaceSet *a = interfaceSetCreate(); + * InterfaceSet *b = interfaceSetCreate(); + * + * if (interfaceSetEquals(a, b)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +bool interfaceSetEquals(const InterfaceSet *a, const InterfaceSet *b); +#endif // InterfaceSet_h diff --git a/hicn-light/src/utils/punting.c b/hicn-light/src/utils/punting.c new file mode 100755 index 000000000..9352732fe --- /dev/null +++ b/hicn-light/src/utils/punting.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include +#include + +struct punting { + char *symbolic; + Address *prefix; + uint32_t len; +}; + +Punting *puntingCreate(const char *listenerName, Address *prefix, + uint32_t len) { + parcAssertNotNull(listenerName, "Parameter listenerName must be non-null"); + parcAssertNotNull(prefix, "Parameter prefix must be non-null"); + + Punting *punting = parcMemory_AllocateAndClear(sizeof(Punting)); + if (punting) { + punting->symbolic = + parcMemory_StringDuplicate(listenerName, strlen(listenerName)); + punting->prefix = addressCopy(prefix); + punting->len = len; + } + + return punting; +} + +void puntingRelease(Punting **puntingPtr) { + parcAssertNotNull(puntingPtr, + "Parameter puntingPtr must be non-null double pointer"); + parcAssertNotNull(*puntingPtr, + "Parameter puntingPtr dereference to non-null pointer"); + + Punting *punting = *puntingPtr; + + if (punting->symbolic) { + parcMemory_Deallocate((void **)&punting->symbolic); + } + + if (punting->prefix) { + addressDestroy(&punting->prefix); + } + + parcMemory_Deallocate((void **)&punting); + *puntingPtr = NULL; +} + +bool puntingEquals(const Punting *a, const Punting *b) { + if ((a == NULL && b == NULL) || a == b) { + // both null or identically equal + return true; + } + + if (a == NULL || b == NULL) { + // only one is null + return false; + } + + if ((strcmp(a->symbolic, b->symbolic) == 0) && + (addressEquals(a->prefix, b->prefix)) && (a->len == b->len)) { + return true; + } + + return false; +} + +const char *puntingGetSymbolicName(const Punting *punting) { + parcAssertNotNull(punting, "Parameter listener must be non-null"); + return punting->symbolic; +} + +Address *puntingGetAddress(const Punting *punting) { + parcAssertNotNull(punting, "Parameter listener must be non-null"); + return punting->prefix; +} + +uint32_t puntingPrefixLen(const Punting *punting) { + parcAssertNotNull(punting, "Parameter listener must be non-null"); + return punting->len; +} diff --git a/hicn-light/src/utils/punting.h b/hicn-light/src/utils/punting.h new file mode 100755 index 000000000..03841c5ad --- /dev/null +++ b/hicn-light/src/utils/punting.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef punting_h +#define punting_h + +struct punting; +typedef struct punting Punting; + +#include + +/** + * Creates a Punting object + * + * The symbolic name represents this listener and may be used by other commands. + * It must be unique, otherwise the command will fail when sent to the + * forwarder. + * + * @param [in] symbolic name of the listener + * @param [in] prefix address to add to the punting rule + * @param [in] len prefix length + * + * @return non-null An Allocated object + * @return null An error + * + */ +Punting *puntingCreate(const char *symbolic, Address *prefix, uint32_t len); + +/** + * Releases a reference count to the object + * + * <#Paragraphs Of Explanation#> + * + * @param [in,out] etherConnPtr A pointer to an etherConn object, will be + * null'd. + * + */ +void puntingRelease(Punting **puntingPtr); + +/** + * Determine if two light Punting are equal. + * + */ + +bool puntingEquals(const Punting *a, const Punting *b); + +/** + * Returns the symbolic name + * + */ +const char *puntingGetSymbolicName(const Punting *punting); + +/** + * Returns the address (INET or INET6 ip address) + * + */ +Address *puntingGetAddress(const Punting *punting); + +uint32_t puntingPrefixLen(const Punting *punting); +#endif // punting_h diff --git a/hicn-light/src/utils/utils.c b/hicn-light/src/utils/utils.c new file mode 100755 index 000000000..a41478a4b --- /dev/null +++ b/hicn-light/src/utils/utils.c @@ -0,0 +1,258 @@ +// Utility function for commands + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +// This is the unique sequence number used by all messages and its thread locks +static pthread_mutex_t nextSequenceNumberMutex = PTHREAD_MUTEX_INITIALIZER; +static uint32_t nextSequenceNumber = 1; + +uint32_t utils_GetNextSequenceNumber(void) { + uint32_t seqnum; + + int result = pthread_mutex_lock(&nextSequenceNumberMutex); + parcAssertTrue(result == 0, "Got error from pthread_mutex_lock: %d", result); + + seqnum = nextSequenceNumber++; + + result = pthread_mutex_unlock(&nextSequenceNumberMutex); + parcAssertTrue(result == 0, "Got error from pthread_mutex_unlock: %d", + result); + + return seqnum; +} + +/** + * Return true if string is purely an integer + */ +bool utils_IsNumber(const char *string) { + size_t len = strlen(string); + for (size_t i = 0; i < len; i++) { + if (!isdigit(string[i])) { + return false; + } + } + return true; +} + +/** + * A symbolic name must be at least 1 character and must begin with an alpha. + * The remainder must be an alphanum. + */ +bool utils_ValidateSymbolicName(const char *symbolic) { + bool success = false; + size_t len = strlen(symbolic); + if (len > 0) { + if (isalpha(symbolic[0])) { + success = true; + for (size_t i = 1; i < len; i++) { + if (!isalnum(symbolic[i])) { + success = false; + break; + } + } + } + } + return success; +} + +Address *utils_AddressFromInet(in_addr_t *addr4, in_port_t *port) { + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = *port; + addr.sin_addr.s_addr = *addr4; + + Address *result = addressCreateFromInet(&addr); + return result; +} + +Address *utils_AddressFromInet6(struct in6_addr *addr6, in_port_t *port) { + struct sockaddr_in6 addr; + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_port = *port; + addr.sin6_addr = *addr6; + addr.sin6_scope_id = 0; + // Other 2 fields: scope_id and flowinfo, do not know what to put inside. + + Address *result = addressCreateFromInet6(&addr); + return result; +} + +struct iovec *utils_CreateAck(header_control_message *header, void *payload, + size_t payloadLen) { + struct iovec *response = + parcMemory_AllocateAndClear(sizeof(struct iovec) * 2); + + header->messageType = ACK_LIGHT; + + response[0].iov_base = header; + response[0].iov_len = sizeof(header_control_message); + response[1].iov_base = payload; + response[1].iov_len = payloadLen; + + return response; +} + +struct iovec *utils_CreateNack(header_control_message *header, void *payload, + size_t payloadLen) { + struct iovec *response = + parcMemory_AllocateAndClear(sizeof(struct iovec) * 2); + + header->messageType = NACK_LIGHT; + + response[0].iov_base = header; + response[0].iov_len = sizeof(header_control_message); + response[1].iov_base = payload; + response[1].iov_len = payloadLen; + + return response; +} + +char *utils_BuildStringFromInet(in_addr_t *addr4, in_port_t *port) { + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = *port; + addr.sin_addr.s_addr = *addr4; + + PARCBufferComposer *composer = parcBufferComposer_Create(); + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer( + parcNetwork_SockInet4Address_BuildString(&addr, composer)); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + parcBufferComposer_Release(&composer); + return result; +} + +char *utils_BuildStringFromInet6(struct in6_addr *addr6, in_port_t *port) { + struct sockaddr_in6 addr; + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_port = *port; + addr.sin6_addr = *addr6; + + PARCBufferComposer *composer = parcBufferComposer_Create(); + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer( + parcNetwork_SockInet6Address_BuildString(&addr, composer)); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + parcBufferComposer_Release(&composer); + return result; +} + +char *utils_CommandAddressToString(address_type addressType, + union commandAddr *address, + in_port_t *port) { + char *result; + + switch (addressType) { + case ADDR_INET: { + result = utils_BuildStringFromInet(&address->ipv4, port); + break; + } + + case ADDR_INET6: { + result = utils_BuildStringFromInet6(&address->ipv6, port); + break; + } + + default: { + char *addrStr = (char *)parcMemory_Allocate(sizeof(char) * 32); + sprintf(addrStr, "Error: UNKNOWN address type = %d", addressType); + result = addrStr; + break; + } + } + return result; +} + +struct iovec *utils_SendRequest(ControlState *state, command_id command, + void *payload, size_t payloadLen) { + bool success = false; + + // get sequence number for the header + uint32_t currentSeqNum = utils_GetNextSequenceNumber(); + + // Allocate and fill the header + header_control_message *headerControlMessage = + parcMemory_AllocateAndClear(sizeof(header_control_message)); + headerControlMessage->messageType = REQUEST_LIGHT; + headerControlMessage->commandID = command; + headerControlMessage->seqNum = currentSeqNum; + if (payloadLen > 0) { + headerControlMessage->length = 1; + } + + struct iovec msg[2]; + msg[0].iov_base = headerControlMessage; + msg[0].iov_len = sizeof(header_control_message); + msg[1].iov_base = payload; + msg[1].iov_len = payloadLen; + + struct iovec *response = controlState_WriteRead(state, msg); + + header_control_message *receivedHeader = + (header_control_message *)response[0].iov_base; + if (receivedHeader->seqNum != currentSeqNum) { + printf("Seq number is NOT correct: expected %d got %d \n", currentSeqNum, + receivedHeader->seqNum); + // failure + } else { + if (receivedHeader->messageType == RESPONSE_LIGHT) { + return response; // command needs both payload and header + } else { + if (receivedHeader->messageType == ACK_LIGHT) { + success = true; + } else if (receivedHeader->messageType == NACK_LIGHT) { + success = true; + } else { + printf("Error: unrecognized message type"); // failure + } + } + } + + // deallocate when payload & header of the response are not needed + if (receivedHeader->length > 0) { + parcMemory_Deallocate(&response[1].iov_base); // free received payload + } + parcMemory_Deallocate(&response[0].iov_base); // free receivedHeader + + // return response + if (success) { + return response; + } else { + parcMemory_Deallocate(&response); // free iovec pointer + return NULL; // will generate a failure + } +} + +const char *utils_PrefixLenToString(address_type addressType, + union commandAddr *address, + uint8_t *prefixLen) { + char len[4]; // max size + 1 + sprintf(len, "%u", (unsigned)*prefixLen); + in_port_t port = htons(1234); // this is a random port number that is ignored + + char *prefix = utils_CommandAddressToString(addressType, address, &port); + char *prefixStr = malloc(strlen(prefix) + strlen(len) + 2); + strcpy(prefixStr, prefix); + strcat(prefixStr, "/"); + strcat(prefixStr, len); + + free(prefix); + + return prefixStr; +} diff --git a/hicn-light/src/utils/utils.h b/hicn-light/src/utils/utils.h new file mode 100755 index 000000000..1d2616941 --- /dev/null +++ b/hicn-light/src/utils/utils.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef utils_h +#define utils_h + +#include +#include +#include + +/** + * Return true if string is purely an integer + */ +bool utils_IsNumber(const char *string); + +/** + * A symbolic name must be at least 1 character and must begin with an alpha. + * The remainder must be an alphanum. + */ +bool utils_ValidateSymbolicName(const char *symbolic); + +/** + * Convert an internet address family (IPv4) to the address format used by the + * Fwd + */ +Address *utils_AddressFromInet(in_addr_t *addr4, in_port_t *port); + +/** + * Convert an internet address family (IPv6) to the address format used by the + * Fwd + */ +Address *utils_AddressFromInet6(struct in6_addr *addr6, in_port_t *port); + +/** + *Create an Ack message instance as a response of a control successfully + *completed. + */ +struct iovec *utils_CreateAck(header_control_message *header, void *payload, + size_t payloadLen); + +/** + *Create a Nack message instance as a response of a control unsuccessfully + *completed. + */ +struct iovec *utils_CreateNack(header_control_message *header, void *payload, + size_t payloadLen); + +/** + *Convert IPv4/IPv6 address from binary to text string. `uint8_t *ipAddress` has + *to be a `in_addr_t * or `a struct in6_addr *. + */ +char *utils_CommandAddressToString(address_type addressType, + union commandAddr *address, in_port_t *port); + +/** + *Given a command payload, it generates the header and send the request to the + *deamon. + */ +struct iovec *utils_SendRequest(ControlState *state, command_id command, + void *payload, size_t payloadLen); + +/** + *Convert a IPv4/IPv6 address plus Netmask len from binary to text string in the + *form [add]:[port]/[len]. + */ +const char *utils_PrefixLenToString(address_type addressType, + union commandAddr *address, + uint8_t *prefixLen); + +#endif \ No newline at end of file diff --git a/hicn-plugin/AUTHORS b/hicn-plugin/AUTHORS new file mode 100755 index 000000000..9a3da8e21 --- /dev/null +++ b/hicn-plugin/AUTHORS @@ -0,0 +1,4 @@ +hicn-plugin authors are listed below: + + Alberto Compagno + Luca Muscariello \ No newline at end of file diff --git a/hicn-plugin/CMakeLists.txt b/hicn-plugin/CMakeLists.txt new file mode 100755 index 000000000..f58ecb061 --- /dev/null +++ b/hicn-plugin/CMakeLists.txt @@ -0,0 +1,235 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) +project(hicn-plugin) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/../cmake/Modules/") +set (CMAKE_CXX_STANDARD 11) +set (CMAKE_C_STANDARD 11) + +# Check for memfd_create syscall +include(CheckSymbolExists) +CHECK_SYMBOL_EXISTS ( "__NR_memfd_create" "sys/syscall.h" HAVE_MEMFD_CREATE ) +if ( HAVE_MEMFD_CREATE ) + add_definitions ( -DHAVE_MEMFD_CREATE ) +endif() + +# Dependencies + +find_package(Vpp REQUIRED) + +include_directories(${VPP_INCLUDE_DIR}) + +set(LIBHICN_FILES + ../lib/src/mapme.c + ../lib/src/name.c + ../lib/src/ops.c + ../lib/src/protocol/ah.c + ../lib/src/protocol/icmp.c + ../lib/src/protocol/ipv4.c + ../lib/src/protocol/ipv6.c + ../lib/src/protocol/tcp.c +) + +set(LIBHICN_HEADER_FILES_SRC + ../lib/src/hicn.h + ../lib/src/base.h + ../lib/src/common.h + ../lib/src/error.h + ../lib/src/header.h + ../lib/src/name.h + ../lib/src/protocol.h + ../lib/src/ops.h + ../lib/src/mapme.h +) + +set(LIBHICN_HEADER_FILES_PROTOCOL + ../lib/src/protocol/ah.h + ../lib/src/protocol/icmp.h + ../lib/src/protocol/icmprd.h + ../lib/src/protocol/ipv4.h + ../lib/src/protocol/ipv6.h + ../lib/src/protocol/tcp.h + ../lib/src/protocol/udp.h +) + +set(HICN_PLUGIN_SOURCE_FILES + src/hicn.c + src/hicn_api.c + src/cli.c + src/hashtb.c + src/mgmt.c + src/pcs.c + src/route.c + src/strategy_dpo_manager.c + src/strategy.c + src/interest_pcslookup_node.c + src/interest_hitpit_node.c + src/interest_hitcs_node.c + src/data_pcslookup_node.c + src/data_fwd_node.c + src/data_push_node.c + src/error.c + src/faces/face_cli.c + src/faces/face.c + src/faces/ip/face_ip.c + src/faces/ip/face_ip_cli.c + src/faces/ip/face_ip_node.c + src/faces/ip/iface_ip_node.c + src/faces/ip/dpo_ip.c + src/faces/udp/face_udp.c + src/faces/udp/face_udp_cli.c + src/faces/udp/face_udp_node.c + src/faces/udp/iface_udp_node.c + src/faces/udp/dpo_udp.c + src/faces/app/address_mgr.c + src/faces/app/face_cons.c + src/faces/app/face_prod.c + src/faces/app/face_prod_node.c + src/faces/app/face_app_cli.c + src/punt.c + src/pg.c + src/strategies/dpo_mw.c + src/strategies/strategy_mw.c + src/strategies/strategy_mw_cli.c + src/cache_policies/cs_lru.c + src/mapme_ack_node.c + src/mapme_ctrl_node.c + src/mapme_eventmgr.c +) + +set(HICN_PLUGIN_HEADER_FILES + src/hicn_all_api_h.h + src/hashtb.h + src/mgmt.h + src/params.h + src/pcs.h + src/hicn_api.h + src/hicn.h + src/state.h + src/infra.h + src/hicn_msg_enum.h + src/parser.h + src/route.h + src/strategy_dpo_ctx.h + src/strategy_dpo_manager.h + src/strategy.h + src/interest_pcslookup.h + src/interest_hitpit.h + src/interest_hitcs.h + src/data_pcslookup.h + src/data_fwd.h + src/error.h + src/face_db.h + src/faces/face.h + src/faces/ip/face_ip.h + src/faces/ip/face_ip_node.h + src/faces/ip/iface_ip_node.h + src/faces/ip/dpo_ip.h + src/faces/udp/face_udp.h + src/faces/udp/face_udp_node.h + src/faces/udp/iface_udp_node.h + src/faces/udp/dpo_udp.h + src/faces/app/address_mgr.h + src/faces/app/face_cons.h + src/faces/app/face_prod.h + src/punt.h + src/pg.h + src/strategies/dpo_mw.h + src/strategies/strategy_mw.h + src/cache_policies/cs_policy.h + src/cache_policies/cs_lru.h + src/mapme.h + src/mapme_ack.h + src/mapme_ctrl.h + src/mapme_eventmgr.h +) + +set(HICN_API_TEST_SOURCE_FILES + src/hicn_api_test.c + src/error.c) + +set(HICN_API_TEST_HEADER_FILES + src/hicn_msg_enum.h + src/hicn_all_api_h.h + src/hicn_api.h + src/error.h) + +set(HICN_API_GENERATED_FILES + ${CMAKE_CURRENT_BINARY_DIR}/vpp_plugins/hicn/hicn.api.h) + +if (NOT VPP_HOME) + set(VPP_HOME /usr) +endif() + +if (NOT CMAKE_BUILD_TYPE) + set (CMAKE_BUILD_TYPE "Release") +endif (NOT CMAKE_BUILD_TYPE) + +SET(HICN_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}/lib CACHE STRING "hicn_install_prefix") + +if (CMAKE_BUILD_TYPE STREQUAL "Release") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Wall -march=native -O3 -g") +elseif (CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall -march=native -O0 -g") + add_definitions(-DCLIB_DEBUG -fPIC -fstack-protector-all) +endif() + +execute_process(COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/hicn) +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/vpp_plugins/hicn/hicn.api.h + COMMAND ${VPP_HOME}/bin/vppapigen --input ${CMAKE_CURRENT_SOURCE_DIR}/src/hicn.api --output ${CMAKE_CURRENT_BINARY_DIR}/vpp_plugins/hicn/hicn.api.h + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/hicn.api) + +include_directories(SYSTEM) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DHICN_VPP_PLUGIN=1") +add_library(hicn_plugin SHARED + ${LIBHICN_FILES} + ${HICN_PLUGIN_SOURCE_FILES} + ${HICN_API_GENERATED_FILES}) + +file(COPY ${HICN_API_TEST_HEADER_FILES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/vpp_plugins/hicn) +include_directories(${CMAKE_CURRENT_BINARY_DIR}/vpp_plugins) + +file(COPY ${LIBHICN_HEADER_FILES_SRC} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/hicn) +file(COPY ${LIBHICN_HEADER_FILES_PROTOCOL} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/hicn/protocol) + +add_library(hicn_api_test_plugin SHARED + ${HICN_API_TEST_SOURCE_FILES} + ${HICN_API_GENERATED_FILES}) + +set(VPP_INSTALL_PLUGIN ${HICN_INSTALL_PREFIX}/vpp_plugins) +set(VPP_INSTALL_API_TEST_PLUGIN ${HICN_INSTALL_PREFIX}/vpp_api_test_plugins CACHE STRING "vpp_install_api_test_plugin") + +set_target_properties(hicn_plugin + PROPERTIES + LINKER_LANGUAGE C + INSTALL_RPATH ${VPP_INSTALL_PLUGIN} + PREFIX "") +set_target_properties(hicn_api_test_plugin + PROPERTIES + LINKER_LANGUAGE C + PREFIX "") + +install(DIRECTORY DESTINATION ${VPP_INSTALL_PLUGIN}) +install(TARGETS hicn_plugin + DESTINATION + ${VPP_INSTALL_PLUGIN}) + +install(DIRECTORY DESTINATION ${VPP_INSTALL_API_TEST_PLUGIN}) +install(TARGETS hicn_api_test_plugin + DESTINATION + ${VPP_INSTALL_API_TEST_PLUGIN}) + +install(FILES ${HICN_API_TEST_HEADER_FILES} ${HICN_API_GENERATED_FILES} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/vpp_plugins/hicn) diff --git a/hicn-plugin/README.md b/hicn-plugin/README.md new file mode 100755 index 000000000..448130b51 --- /dev/null +++ b/hicn-plugin/README.md @@ -0,0 +1,168 @@ +Hybrid ICN project: VPP plugin +============================== + +The hICN-plugin forwarder + +## Introduction ## + +A high-performance Hybrid ICN forwarder as a plugin to VPP. + +The plugin provides the following functionalities: + + - Fast packet processing + - Interest aggregation + - Content caching + - Forwarding strategies + +## Quick Start ## +``` +From the code tree root + +(VPP installed with DEB pkg) +$ cd hicn-plugin +$ mkdir -p build +$ cd build +$ cmake .. -DCMAKE_INSTALL_PREFIX=/usr +$ make +$ sudo make install + +(VPP source code -- build type RELEASE) +$ cd hicn-plugin +$ mkdir -p build +$ cd build +$ cmake .. -DVPP_HOME=/build-root/install-vpp-native/vpp/include/ -DHICN_INSTALL_PREFIX=/build-root/install-vpp-native/vpp/lib +$ make +$ sudo make install + +(VPP source code -- build type DEBUG) +$ cd hicn-plugin +$ mkdir -p build +$ cd build +$ cmake .. -DCMAKE_BUILD_TYPE=DEBUG -DVPP_HOME=/build-root/install-vpp_debug-native/vpp/include/ -DHICN_INSTALL_PREFIX=/build-root/install-vpp_debug-native/vpp/lib +$ make +$ sudo make install + +CMAKE variables: +- HICN_INSTALL_PREFIX -- set the install directory for the hicn-plugin. This is the common path to the folders vpp_plugins and vpp_api_test_plugins. Default is /lib +- VPP_INSTALL_PLUGIN -- set the install directory for the libhicn_plugin.so. Defatuls id $HICN_INSTALL_PREFIX/vpp_plugins +- HICN_API_TEST_HEADER_FILES -- set the install directory for the header files. Default is /include/vpp_plugins/hicn +``` + +## Using hICN plugin ## + +### Platforms ### + +hICN-plugin has been tested in: + +- Ubuntu 16.04 LTS (x86_64) +- Ubuntu 18.04 LTS (x86_64) +- Debian Stable/Testing +- Red Hat Enterprise Linux 7 +- CentOS 7 + + +### Dependencies ### + +Build dependencies: + +- VPP 19.01 + - DEB packages: + - vpp + - vpp-lib + - vpp-dev + - vpp-plugins + +Hardware support: + +- [DPDK](http://DPDK.org/) compatible nic + +### Getting started ### +In order to start, the hICN plugin requires a running instance of VPP +The steps required to successfully start hICN are: + +- Setup the host to run VPP +- Configure VPP to use DPDK compatible nics +- Start VPP +- Configure VPP interfaces +- Configure and start hICN + +Detailed information for configuring VPP can be found at [https://wiki.fd.io/view/VPP](https://wiki.fd.io/view/VPP). + +##### Setup the host for VPP ##### + +Hugepages must be enabled in the system + +``` +$ sudo sysctl -w vm.nr_hugepages=1024 +``` + +In order to use a DPDK interface, the package vpp-dpdk-dkms must be installed in the system and the `uio` and `igb_uio` modules need to be loaded in the kernel + +``` +$ sudo apt install vpp-dpdk-dkms +$ sudo modprobe uio +$ sudo modprobe igb_uio +``` + +If the DPDK interface we want to assign to VPP is up, we must bring it down + +``` +$ sudo ifconfig down +``` + +##### Configure VPP ##### +The file /etc/VPP/startup.conf contains a set of parameters to setup VPP at startup. +The following example sets up VPP to use a DPDK interfaces: + +``` shell +unix { + nodaemon + log /tmp/vpp.log + full-coredump +} + +api-trace { + on +} + +api-segment { + gid vpp +} + +dpdk { + dev 0000:08:00.0 +} +``` +Where `0000:08:00.0` must be replaced with the actual PCI address of the DPDK interface + +##### Start VPP ##### + +VPP can be started as a process or a service: + +``` shell +Start VPP as a service in Ubuntu 16.04 +$ sudo systemctl start vpp + +Start VPP as a process in both 16.04 +$ sudo vpp -c /etc/vpp/startup.conf + +``` + +## License ## + +This software is distributed under the following license: + +``` +Copyright (c) 2017-2019 Cisco and/or its affiliates. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` \ No newline at end of file diff --git a/hicn-plugin/src/cache_policies/cs_lru.c b/hicn-plugin/src/cache_policies/cs_lru.c new file mode 100755 index 000000000..f35bee3c9 --- /dev/null +++ b/hicn-plugin/src/cache_policies/cs_lru.c @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "../hashtb.h" +#include "../strategy_dpo_manager.h" +#include "../error.h" +#include "cs_lru.h" +#include "cs_policy.h" + +hicn_cs_policy_vft_t hicn_cs_lru = { + .hicn_cs_insert = &hicn_cs_lru_insert, + .hicn_cs_update = &hicn_cs_lru_update_head, + .hicn_cs_dequeue = &hicn_cs_lru_dequeue, + .hicn_cs_delete_get = &hicn_cs_lru_delete_get, + .hicn_cs_trim = &hicn_cs_lru_trim, +}; + +/* + * Insert a new CS element at the head of the CS LRU + */ +void +hicn_cs_lru_insert (hicn_pit_cs_t * p, hicn_hash_node_t * node, + hicn_pcs_entry_t * pcs, hicn_cs_policy_t * policy_state) +{ + hicn_hash_node_t *lrunode; + hicn_pcs_entry_t *lrupcs; + u32 idx; + + idx = hicn_hashtb_node_idx_from_node (p->pcs_table, node); + + if (policy_state->head != 0) + { + lrunode = hicn_hashtb_node_from_idx (p->pcs_table, policy_state->head); + lrupcs = hicn_pit_get_data (lrunode); + + ASSERT (lrupcs->u.cs.cs_lru_prev == 0); + lrupcs->u.cs.cs_lru_prev = idx; + + pcs->u.cs.cs_lru_prev = 0; + pcs->u.cs.cs_lru_next = policy_state->head; + + policy_state->head = idx; + } + else + { + ASSERT (policy_state->tail == 0); /* We think the list is + * empty */ + + policy_state->head = policy_state->tail = idx; + + pcs->u.cs.cs_lru_next = pcs->u.cs.cs_lru_prev = 0; + } + + policy_state->count++; +} + +void +hicn_cs_lru_delete_get (hicn_pit_cs_t * p, hicn_cs_policy_t * policy_state, + hicn_hash_node_t ** nodep, + hicn_pcs_entry_t ** pcs_entry, + hicn_hash_entry_t ** hash_entry) +{ + *nodep = hicn_hashtb_node_from_idx (p->pcs_table, policy_state->tail); + *pcs_entry = hicn_pit_get_data (*nodep); + + *hash_entry = hicn_hashtb_get_entry (p->pcs_table, (*nodep)->entry_idx, + (*nodep)->bucket_id, + (*nodep)->hn_flags & + HICN_HASH_NODE_OVERFLOW_BUCKET); +} + +/* + * Dequeue an LRU element, for example when it has expired. + */ +void +hicn_cs_lru_dequeue (hicn_pit_cs_t * pit, hicn_hash_node_t * pnode, + hicn_pcs_entry_t * pcs, hicn_cs_policy_t * lru) +{ + hicn_hash_node_t *lrunode; + hicn_pcs_entry_t *lrupcs; + + if (pcs->u.cs.cs_lru_prev != 0) + { + /* Not already on the head of the LRU */ + lrunode = hicn_hashtb_node_from_idx (pit->pcs_table, + pcs->u.cs.cs_lru_prev); + lrupcs = hicn_pit_get_data (lrunode); + + lrupcs->u.cs.cs_lru_next = pcs->u.cs.cs_lru_next; + } + else + { + ASSERT (lru->head == + hicn_hashtb_node_idx_from_node (pit->pcs_table, pnode)); + lru->head = pcs->u.cs.cs_lru_next; + } + + if (pcs->u.cs.cs_lru_next != 0) + { + /* Not already the end of the LRU */ + lrunode = hicn_hashtb_node_from_idx (pit->pcs_table, + pcs->u.cs.cs_lru_next); + lrupcs = hicn_pit_get_data (lrunode); + + lrupcs->u.cs.cs_lru_prev = pcs->u.cs.cs_lru_prev; + } + else + { + /* This was the last LRU element */ + ASSERT (lru->tail == + hicn_hashtb_node_idx_from_node (pit->pcs_table, pnode)); + lru->tail = pcs->u.cs.cs_lru_prev; + } + + pcs->u.cs.cs_lru_next = pcs->u.cs.cs_lru_prev = 0; + lru->count--; +} + +/* + * Move a CS LRU element to the head, probably after it's been used. + */ +void +hicn_cs_lru_update_head (hicn_pit_cs_t * pit, hicn_hash_node_t * pnode, + hicn_pcs_entry_t * pcs, hicn_cs_policy_t * lru) +{ + if (pcs->u.cs.cs_lru_prev != 0) + { + /* + * Not already on the head of the LRU, detach it from its + * current position + */ + hicn_cs_lru_dequeue (pit, pnode, pcs, lru); + + /* Now detached from the list; attach at head */ + hicn_cs_lru_insert (pit, pnode, pcs, lru); + + } + else + { + ASSERT (lru->head == + hicn_hashtb_node_idx_from_node (pit->pcs_table, pnode)); + } +} + +/* + * Remove a batch of nodes from the CS LRU, copying their node indexes into + * the caller's array. We expect this is done when the LRU size exceeds the + * CS's limit. Return the number of removed nodes. + */ +int +hicn_cs_lru_trim (hicn_pit_cs_t * pit, u32 * node_list, int sz, + hicn_cs_policy_t * lru) +{ + hicn_hash_node_t *lrunode; + hicn_pcs_entry_t *lrupcs; + u32 idx; + int i; + + idx = lru->tail; + + for (i = 0; i < sz; i++) + { + + if (idx == 0) + { + break; + } + lrunode = hicn_hashtb_node_from_idx (pit->pcs_table, idx); + lrupcs = hicn_pit_get_data (lrunode); + + node_list[i] = idx; + + idx = lrupcs->u.cs.cs_lru_prev; + lrupcs->u.cs.cs_lru_prev = 0; + lrupcs->u.cs.cs_lru_next = 0; + } + + lru->count -= i; + + lru->tail = idx; + if (idx != 0) + { + lrunode = hicn_hashtb_node_from_idx (pit->pcs_table, idx); + lrupcs = hicn_pit_get_data (lrunode); + + lrupcs->u.cs.cs_lru_next = 0; + } + else + { + /* If the tail is empty, the whole lru is empty */ + lru->head = 0; + } + + return (i); +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/cache_policies/cs_lru.h b/hicn-plugin/src/cache_policies/cs_lru.h new file mode 100755 index 000000000..94320f7f9 --- /dev/null +++ b/hicn-plugin/src/cache_policies/cs_lru.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LRU_H__ +#define __LRU_H__ + +#include "../pcs.h" +#include "../hashtb.h" +#include "cs_policy.h" + +extern hicn_cs_policy_vft_t hicn_cs_lru; + +/* + * Insert a new CS element at the head of the CS LRU + */ +void +hicn_cs_lru_insert (hicn_pit_cs_t * pcs, hicn_hash_node_t * pnode, + hicn_pcs_entry_t * entry, hicn_cs_policy_t * lru); + + +/* + * Dequeue an LRU element, for example when it has expired. + */ +void +hicn_cs_lru_dequeue (hicn_pit_cs_t * pcs, hicn_hash_node_t * pnode, + hicn_pcs_entry_t * entry, hicn_cs_policy_t * lru); + +/* + * Move a CS LRU element to the head, probably after it's been used. + */ +void +hicn_cs_lru_update_head (hicn_pit_cs_t * pcs, hicn_hash_node_t * pnode, + hicn_pcs_entry_t * entry, hicn_cs_policy_t * lru); + +void +hicn_cs_lru_delete_get (hicn_pit_cs_t * p, hicn_cs_policy_t * policy, + hicn_hash_node_t ** node, hicn_pcs_entry_t ** pcs, + hicn_hash_entry_t ** hash_entry); + +/* + * Remove a batch of nodes from the CS LRU, copying their node indexes into + * the caller's array. We expect this is done when the LRU size exceeds the + * CS's limit. Return the number of removed nodes. + */ +int hicn_cs_lru_trim (hicn_pit_cs_t * pcs, u32 * node_list, int sz, + hicn_cs_policy_t * lru); + + +#endif /* // __LRU_H__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/cache_policies/cs_policy.h b/hicn-plugin/src/cache_policies/cs_policy.h new file mode 100755 index 000000000..08817de18 --- /dev/null +++ b/hicn-plugin/src/cache_policies/cs_policy.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_CS_POLICY_H__ +#define __HICN_CS_POLICY_H__ + +#include "../hashtb.h" + +/* + * Structure + */ +typedef struct hicn_cs_policy_s +{ + u32 max; + u32 count; + + /* Indexes to hashtable nodes forming CS LRU */ + u32 head; + u32 tail; + +} hicn_cs_policy_t; + +/* Forward declaration */ +struct hicn_pit_cs_s; +struct hicn_hash_node_s; +struct hicn_pcs_entry_s; +struct hicn_cs_policy_s; + +/** + * @brief Definition of the virtual functin table for a cache policy. + * + * A cache policy must implement three functions: insert, update, delete, trim. + */ +typedef struct hicn_cs_policy_vft_s +{ + void (*hicn_cs_insert) (struct hicn_pit_cs_s * p, + struct hicn_hash_node_s * node, + struct hicn_pcs_entry_s * pcs, + hicn_cs_policy_t * policy); + + void (*hicn_cs_update) (struct hicn_pit_cs_s * p, + struct hicn_hash_node_s * node, + struct hicn_pcs_entry_s * pcs, + hicn_cs_policy_t * policy); + + void (*hicn_cs_dequeue) (struct hicn_pit_cs_s * p, + struct hicn_hash_node_s * node, + struct hicn_pcs_entry_s * pcs, + hicn_cs_policy_t * policy); + + void (*hicn_cs_delete_get) (struct hicn_pit_cs_s * p, + hicn_cs_policy_t * policy, + struct hicn_hash_node_s ** node, + struct hicn_pcs_entry_s ** pcs, + struct hicn_hash_entry_s ** hash_entry); + + int (*hicn_cs_trim) (struct hicn_pit_cs_s * p, u32 * node_list, int sz, + hicn_cs_policy_t * policy); +} hicn_cs_policy_vft_t; + + + +#endif /* // __HICN_POLICY_H__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/cli.c b/hicn-plugin/src/cli.c new file mode 100755 index 000000000..c8c0be4ff --- /dev/null +++ b/hicn-plugin/src/cli.c @@ -0,0 +1,1247 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include // port registration +#include // ip46_address_t +#include + +#include "hicn.h" +#include "infra.h" +#include "parser.h" +#include "mgmt.h" +#include "strategy_dpo_manager.h" +#include "strategy.h" +#include "pg.h" +#include "error.h" +#include "faces/face.h" +#include "route.h" +#include "punt.h" +#include "hicn_api.h" +#include "mapme.h" + +extern ip_version_t ipv4; +extern ip_version_t ipv6; + +static vl_api_hicn_api_node_params_set_t node_ctl_params = { + .pit_max_size = -1, + .pit_dflt_lifetime_sec = -1.0f, + .pit_min_lifetime_sec = -1.0f, + .pit_max_lifetime_sec = -1.0f, + .cs_max_size = -1, + .cs_reserved_app = -1, +}; + +typedef enum +{ + IP, + ETHERNET, +} interface_type_t; + +/* + * Supporting function that return if the interface is IP or ethernet + */ +static interface_type_t +hicn_cli_is_ip_interface (vlib_main_t * vm, + vnet_main_t * vnm, u32 sw_if_index) +{ + + vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, sw_if_index); + + vnet_device_class_t *dev_class = + vnet_get_device_class (vnm, hi->dev_class_index); + if (!strcmp (dev_class->name, "Loopback")) + { + return IP; + } + return ETHERNET; + +} + +/* + * cli handler for 'control start' + */ +static clib_error_t * +hicn_cli_node_ctl_start_set_command_fn (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + int ret; + + ret = hicn_infra_plugin_enable_disable (1 /* enable */ , + node_ctl_params.pit_max_size, + node_ctl_params.pit_dflt_lifetime_sec, + node_ctl_params.pit_min_lifetime_sec, + node_ctl_params.pit_max_lifetime_sec, + node_ctl_params.cs_max_size, + node_ctl_params.cs_reserved_app); + + vlib_cli_output (vm, "hicn: fwdr initialize => %s\n", + get_error_string (ret)); + + return (ret == HICN_ERROR_NONE) ? 0 : clib_error_return (0, + get_error_string + (ret)); +} + +/* + * cli handler for 'control stop' + */ +static clib_error_t * +hicn_cli_node_ctl_stop_set_command_fn (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + int ret; + + /* + * Catch unexpected extra arguments on this line. See comment on + * hicn_cli_node_ctrl_start_set_command_fn + */ + if (main_input->index > 0 && + main_input->buffer[main_input->index - 1] != '\n') + { + unformat_input_t _line_input, *line_input = &_line_input; + if (!unformat_user (main_input, unformat_line_input, line_input)) + { + return (0); + } + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + return clib_error_return (0, "%s '%U'", + get_error_string (HICN_ERROR_CLI_INVAL), + format_unformat_error, line_input); + } + } + ret = hicn_infra_plugin_enable_disable (0 /* !enable */ , + node_ctl_params.pit_max_size, + node_ctl_params.pit_dflt_lifetime_sec, + node_ctl_params.pit_min_lifetime_sec, + node_ctl_params.pit_max_lifetime_sec, + node_ctl_params.cs_max_size, + node_ctl_params.cs_reserved_app); + + return (ret == HICN_ERROR_NONE) ? 0 : clib_error_return (0, + get_error_string + (ret)); +} + +#define DFLTD_RANGE_OK(val, min, max) \ +({ \ + __typeof__ (val) _val = (val); \ + __typeof__ (min) _min = (min); \ + __typeof__ (max) _max = (max); \ + (_val == -1) || \ + (_val >= _min && _val <= _max); \ +}) + +/* + * cli handler for 'control param' + */ +static clib_error_t * +hicn_cli_node_ctl_param_set_command_fn (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + int rv = 0; + + int table_size; + f64 lifetime; + int cs_reserved_app; + + if (hicn_main.is_enabled) + { + return (clib_error_return + (0, "params cannot be altered once hicn started")); + } + /* Get a line of input. */ + unformat_input_t _line_input, *line_input = &_line_input; + if (!unformat_user (main_input, unformat_line_input, line_input)) + { + return clib_error_return (0, + get_error_string + (HICN_ERROR_FWD_ALREADY_ENABLED)); + } + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "pit")) + { + if (unformat (line_input, "size %d", &table_size)) + { + if (!DFLTD_RANGE_OK (table_size, HICN_PARAM_PIT_ENTRIES_MIN, + HICN_PARAM_PIT_ENTRIES_MAX)) + { + rv = HICN_ERROR_PIT_CONFIG_SIZE_OOB; + break; + } + node_ctl_params.pit_max_size = table_size; + } + else if (unformat (line_input, "dfltlife %f", &lifetime)) + { + if (!DFLTD_RANGE_OK + (lifetime, HICN_PARAM_PIT_LIFETIME_BOUND_MIN_SEC, + HICN_PARAM_PIT_LIFETIME_BOUND_MAX_SEC)) + { + rv = HICN_ERROR_PIT_CONFIG_DFTLT_OOB; + break; + } + node_ctl_params.pit_dflt_lifetime_sec = lifetime; + } + else if (unformat (line_input, "minlife %f", &lifetime)) + { + if (!DFLTD_RANGE_OK + (lifetime, HICN_PARAM_PIT_LIFETIME_BOUND_MIN_SEC, + HICN_PARAM_PIT_LIFETIME_BOUND_MAX_SEC)) + { + rv = HICN_ERROR_PIT_CONFIG_MINLT_OOB; + break; + } + node_ctl_params.pit_min_lifetime_sec = lifetime; + } + else if (unformat (line_input, "maxlife %f", &lifetime)) + { + if (!DFLTD_RANGE_OK + (lifetime, HICN_PARAM_PIT_LIFETIME_BOUND_MIN_SEC, + HICN_PARAM_PIT_LIFETIME_BOUND_MAX_SEC)) + { + rv = HICN_ERROR_PIT_CONFIG_MAXLT_OOB; + break; + } + node_ctl_params.pit_max_lifetime_sec = lifetime; + } + else + { + rv = HICN_ERROR_CLI_INVAL; + break; + } + } + else if (unformat (line_input, "cs")) + { + if (unformat (line_input, "size %d", &table_size)) + { + if (!DFLTD_RANGE_OK (table_size, HICN_PARAM_CS_ENTRIES_MIN, + HICN_PARAM_CS_ENTRIES_MAX)) + { + rv = HICN_ERROR_CS_CONFIG_SIZE_OOB; + break; + } + node_ctl_params.cs_max_size = table_size; + } + else if (unformat (line_input, "app %d", &cs_reserved_app)) + { + if (!DFLTD_RANGE_OK (cs_reserved_app, 0, 100)) + { + rv = HICN_ERROR_CS_CONFIG_SIZE_OOB; + break; + } + node_ctl_params.cs_reserved_app = cs_reserved_app; + } + else + { + rv = HICN_ERROR_CLI_INVAL; + break; + } + } + else + { + rv = HICN_ERROR_CLI_INVAL; + break; + } + } + + if (node_ctl_params.cs_max_size == 0) + vlib_cli_output (vm, + "CS size set to 0. Consider disable CS at compilation time for better performances\n"); + + return (rv == HICN_ERROR_NONE) ? 0 : clib_error_return (0, "%s '%U'", + get_error_string + (rv), + format_unformat_error, + line_input); +} + +/* + * cli handler for 'hicn show' + */ +static clib_error_t * +hicn_cli_show_command_fn (vlib_main_t * vm, unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + int face_p = 0, fib_p = 0, all_p, internal_p = 0, strategies_p = 0, ret = + HICN_ERROR_NONE; + + /* Get a line of input. */ + unformat_input_t _line_input, *line_input = &_line_input; + if (unformat_user (main_input, unformat_line_input, line_input)) + { + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "face all")) + { + face_p = 1; + } + else if (unformat (line_input, "internal")) + { + /* + * We consider 'internal' a superset, so + * include 'detail' too + */ + internal_p = 1; + } + else if (unformat (line_input, "strategies")) + { + /* + * We consider 'internal' a superset, so + * include 'detail' too + */ + strategies_p = 1; + } + else + { + ret = HICN_ERROR_CLI_INVAL; + goto done; + } + } + } + /* If nothing specified, show everything */ + if ((face_p == 0) && (fib_p == 0) && (strategies_p == 0)) + { + all_p = 1; + } + if (!hicn_main.is_enabled) + { + if (node_ctl_params.pit_max_size == -1 && + node_ctl_params.pit_dflt_lifetime_sec == -1 && + node_ctl_params.pit_min_lifetime_sec == -1 && + node_ctl_params.pit_max_lifetime_sec == -1 && + node_ctl_params.cs_max_size == -1 && + node_ctl_params.cs_reserved_app == -1) + { + ret = HICN_ERROR_FWD_NOT_ENABLED; + goto done; + } + vlib_cli_output (vm, "Forwarder: %sabled\nPreconfiguration:\n", + hicn_main.is_enabled ? "en" : "dis"); + + if (node_ctl_params.pit_max_size != -1) + { + vlib_cli_output (vm, " PIT:: max entries:%d\n", + node_ctl_params.pit_max_size); + } + if (node_ctl_params.pit_dflt_lifetime_sec != -1) + { + vlib_cli_output (vm, " PIT:: dflt lifetime: %05.3f seconds\n", + node_ctl_params.pit_dflt_lifetime_sec); + } + if (node_ctl_params.pit_min_lifetime_sec != -1) + { + vlib_cli_output (vm, " PIT:: min lifetime: %05.3f seconds\n", + node_ctl_params.pit_min_lifetime_sec); + } + if (node_ctl_params.pit_max_lifetime_sec != -1) + { + vlib_cli_output (vm, " PIT:: max lifetime: %05.3f seconds\n", + node_ctl_params.pit_max_lifetime_sec); + } + if (node_ctl_params.cs_max_size != -1) + { + vlib_cli_output (vm, " CS:: max entries:%d\n", + node_ctl_params.cs_max_size); + } + if (node_ctl_params.cs_reserved_app != -1) + { + vlib_cli_output (vm, " CS:: reserved to app:%d\n", + node_ctl_params.cs_reserved_app); + } + goto done; + } + /* Globals */ + vlib_cli_output (vm, + "Forwarder: %sabled\n" + " PIT:: max entries:%d," + " lifetime default: %05.3f sec (min:%05.3f, max:%05.3f)\n" + " CS:: max entries:%d, network entries:%d, app entries:%d (allocated %d, free %d)\n", + hicn_main.is_enabled ? "en" : "dis", + hicn_infra_pit_size, + ((f64) hicn_main.pit_lifetime_dflt_ms) / SEC_MS, + ((f64) hicn_main.pit_lifetime_min_ms) / SEC_MS, + ((f64) hicn_main.pit_lifetime_max_ms) / SEC_MS, + hicn_infra_cs_size, + hicn_infra_cs_size - hicn_main.pitcs.pcs_app_max, + hicn_main.pitcs.pcs_app_max, + hicn_main.pitcs.pcs_app_count, + hicn_main.pitcs.pcs_app_max - + hicn_main.pitcs.pcs_app_count); + + vl_api_hicn_api_node_stats_get_reply_t rm = { 0, } + , *rmp = &rm; + if (hicn_mgmt_node_stats_get (&rm) == HICN_ERROR_NONE) + { + vlib_cli_output (vm, //compare vl_api_hicn_api_node_stats_get_reply_t_handler block + " PIT entries (now): %d\n" + " CS total entries (now): %d, network entries (now): %d\n" + " Forwarding statistics:\n" + " pkts_processed: %d\n" + " pkts_interest_count: %d\n" + " pkts_data_count: %d\n" + " pkts_from_cache_count: %d\n" + " interests_aggregated: %d\n" + " interests_retransmitted: %d\n", + clib_net_to_host_u64 (rmp->pit_entries_count), + clib_net_to_host_u64 (rmp->cs_entries_count), + clib_net_to_host_u64 (rmp->cs_entries_ntw_count), + clib_net_to_host_u64 (rmp->pkts_processed), + clib_net_to_host_u64 (rmp->pkts_interest_count), + clib_net_to_host_u64 (rmp->pkts_data_count), + clib_net_to_host_u64 (rmp->pkts_from_cache_count), + clib_net_to_host_u64 (rmp->interests_aggregated), + clib_net_to_host_u64 (rmp->interests_retx)); + } + if (face_p || all_p) + { + u8 *strbuf = NULL; + + strbuf = format_hicn_face_all (strbuf, 1, 0); + vlib_cli_output (vm, "%s", strbuf); + + } + if (strategies_p || all_p) + { + u8 *strbuf = NULL; + + strbuf = format_hicn_strategy_list (strbuf, 1, 0); + vlib_cli_output (vm, (char *) strbuf); + } +done: + if (all_p && internal_p && ret == HICN_ERROR_NONE) + { + vlib_cli_output (vm, "Plugin features: cs:%d\n", HICN_FEATURE_CS); + vlib_cli_output (vm, + "Removed CS entries (and freed vlib buffers) %d, Removed PIT entries %d", + hicn_main.pitcs.pcs_cs_dealloc, + hicn_main.pitcs.pcs_pit_dealloc); + + } + return (ret == HICN_ERROR_NONE) ? 0 : clib_error_return (0, "%s\n", + get_error_string + (ret)); +} + +/* + * cli handler for 'fib' + */ +static clib_error_t * +hicn_cli_fib_set_command_fn (vlib_main_t * vm, unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + clib_error_t *cl_err = 0; + + int rv = HICN_ERROR_NONE; + int addpfx = -1; + ip46_address_t prefix; + hicn_face_id_t faceid = HICN_FACE_NULL; + u32 strategy_id; + u8 plen = 0; + + /* Get a line of input. */ + unformat_input_t _line_input, *line_input = &_line_input; + if (!unformat_user (main_input, unformat_line_input, line_input)) + { + return (0); + } + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (addpfx == -1 && unformat (line_input, "add")) + { + addpfx = 1; + } + else if (addpfx == -1 && unformat (line_input, "delete")) + { + addpfx = 0; + } + else if (unformat (line_input, "set strategy %d", &strategy_id)) + { + addpfx = 2; + } + else if (addpfx != -1 + && unformat (line_input, "prefix %U/%d", unformat_ip46_address, + &prefix, IP46_TYPE_ANY, &plen)) + {; + } + else if (addpfx <= 1 && unformat (line_input, "face %u", &faceid)) + {; + } + else + { + cl_err = clib_error_return (0, "%s '%U'", + get_error_string (HICN_ERROR_CLI_INVAL), + format_unformat_error, line_input); + goto done; + } + } + + /* Check parse */ + if (addpfx <= 1 + && ((ip46_address_is_zero (&prefix)) || faceid == HICN_FACE_NULL)) + { + cl_err = + clib_error_return (0, "Please specify prefix and a valid faceid..."); + goto done; + } + /* Check parse */ + if ((ip46_address_is_zero (&prefix)) + || (addpfx == 2 && hicn_dpo_strategy_id_is_valid (strategy_id))) + { + cl_err = clib_error_return (0, + "Please specify prefix and strategy_id..."); + goto done; + } + if (addpfx == 0) + { + if (ip46_address_is_zero (&prefix)) + { + cl_err = clib_error_return (0, "Please specify prefix"); + goto done; + } + if (faceid == HICN_FACE_NULL) + { + rv = hicn_route_del (&prefix, plen); + } + else + { + rv = hicn_route_del_nhop (&prefix, plen, faceid); + } + cl_err = + (rv == HICN_ERROR_NONE) ? NULL : clib_error_return (0, + get_error_string + (rv)); + + } + else if (addpfx == 1) + { + rv = hicn_route_add (&faceid, 1, &prefix, plen); + if (rv == HICN_ERROR_ROUTE_ALREADY_EXISTS) + { + rv = hicn_route_add_nhops (&faceid, 1, &prefix, plen); + } + cl_err = + (rv == HICN_ERROR_NONE) ? NULL : clib_error_return (0, + get_error_string + (rv)); + } + else if (addpfx == 2) + { + rv = hicn_route_set_strategy (&prefix, plen, strategy_id); + cl_err = + (rv == HICN_ERROR_NONE) ? NULL : clib_error_return (0, + get_error_string + (rv)); + } +done: + + return (cl_err); +} + +static clib_error_t * +hicn_cli_punting_command_fn (vlib_main_t * vm, unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + hicn_mgmt_punting_op_e punting_op = HICN_MGMT_PUNTING_OP_NONE; + unsigned int subnet_mask = 0; + ip46_address_t prefix; + u32 sw_if_index = ~0; + int ret = 0; + vnet_main_t *vnm = NULL; + u8 type = HICN_PUNT_IP_TYPE; + u32 src_port = 0, dst_port = 0; + vnm = vnet_get_main (); + + unformat_input_t _line_input, *line_input = &_line_input; + if (!unformat_user (main_input, unformat_line_input, line_input)) + { + return (0); + } + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "add")) + { + punting_op = HICN_MGMT_PUNTING_OP_CREATE; + } + else if (unformat (line_input, "delete")) + { + punting_op = HICN_MGMT_PUNTING_OP_DELETE; + } + else if (unformat (line_input, "intfc %U", + unformat_vnet_sw_interface, vnm, &sw_if_index)) + {; + } + else if (unformat + (line_input, "prefix %U/%d", unformat_ip46_address, + &prefix, IP46_TYPE_ANY, &subnet_mask)) + {; + } + else if (unformat (line_input, "type ip")) + { + type = HICN_PUNT_IP_TYPE; + } + else if (unformat (line_input, "type")) + { + if (unformat (line_input, "udp4")) + { + type = HICN_PUNT_UDP4_TYPE; + } + else if (unformat (line_input, "udp6")) + { + type = HICN_PUNT_UDP6_TYPE; + } + + if (unformat (line_input, "src_port %u", &src_port)) + ; + if (unformat (line_input, "dst_port %u", &dst_port)) + ; + } + else + { + return (clib_error_return (0, "invalid option")); + } + } + + if (punting_op == HICN_MGMT_PUNTING_OP_CREATE + && (ip46_address_is_zero (&prefix) || sw_if_index == ~0)) + { + return (clib_error_return + (0, "Please specify valid prefix and interface")); + } + else if ((punting_op == HICN_MGMT_PUNTING_OP_DELETE) && + ip46_address_is_zero (&prefix)) + { + return (clib_error_return + (0, "Please specify valid prefix and optionally an interface")); + } + else if (punting_op == HICN_MGMT_PUNTING_OP_NONE) + { + return (clib_error_return + (0, "Please specify valid operation, add or delete")); + } + switch (punting_op) + { + case HICN_MGMT_PUNTING_OP_CREATE: + { + if (type == HICN_PUNT_UDP4_TYPE || type == HICN_PUNT_UDP6_TYPE) + { + if (src_port != 0 && dst_port != 0) + ret = + hicn_punt_interest_data_for_udp (vm, &prefix, subnet_mask, + sw_if_index, type, + clib_host_to_net_u16 + (src_port), + clib_host_to_net_u16 + (dst_port)); + else + return (clib_error_return + (0, + "Please specify valid source and destination udp port")); + } + else + { + ret = + hicn_punt_interest_data_for_ethernet (vm, &prefix, subnet_mask, + sw_if_index, type); + } + } + break; + case HICN_MGMT_PUNTING_OP_DELETE: + { + if (sw_if_index != ~0) + { + ip46_address_is_ip4 (&prefix) ? + hicn_punt_enable_disable_vnet_ip4_table_on_intf (vm, + sw_if_index, + 0) : + hicn_punt_enable_disable_vnet_ip6_table_on_intf (vm, + sw_if_index, + 0); + } + else if (!(ip46_address_is_zero (&prefix))) + { + ret = ip46_address_is_ip4 (&prefix) ? + hicn_punt_remove_ip4_address (vm, &(prefix.ip4), subnet_mask, 1, + sw_if_index, + 0) : + hicn_punt_remove_ip6_address (vm, (ip6_address_t *) & prefix, + subnet_mask, 1, sw_if_index, 0); + } + } + break; + default: + break; + } + + return (ret == HICN_ERROR_NONE) ? 0 : clib_error_return (0, + get_error_string + (ret)); +} + +static clib_error_t * +hicn_cli_mapme_command_fn (vlib_main_t * vm, unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + hicn_mgmt_mapme_op_e mapme_op = HICN_MGMT_MAPME_OP_NONE; + unsigned int subnet_mask = 0; + ip46_address_t prefix; + u32 sw_if_index = ~0; + int ret = 0; + vnet_main_t *vnm = NULL; + + vnm = vnet_get_main (); + + unformat_input_t _line_input, *line_input = &_line_input; + if (!unformat_user (main_input, unformat_line_input, line_input)) + { + return (0); + } + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "add")) + { + mapme_op = HICN_MGMT_MAPME_OP_CREATE; + } + else if (unformat (line_input, "delete")) + { + mapme_op = HICN_MGMT_MAPME_OP_DELETE; + } + else if (unformat (line_input, "intfc %U", + unformat_vnet_sw_interface, vnm, &sw_if_index)) + {; + } + else if (unformat + (line_input, "prefix %U/%d", unformat_ip46_address, + &prefix, IP46_TYPE_ANY, &subnet_mask)) + {; + } + else + { + return (clib_error_return (0, "invalid option")); + } + } + + if (mapme_op == HICN_MGMT_MAPME_OP_CREATE + && (ip46_address_is_zero (&prefix) || sw_if_index == ~0)) + { + return (clib_error_return + (0, "Please specify valid prefix and interface")); + } + else if ((mapme_op == HICN_MGMT_MAPME_OP_DELETE) && + ip46_address_is_zero (&prefix)) + { + return (clib_error_return + (0, "Please specify valid prefix and optionally an interface")); + } + else if (mapme_op == HICN_MGMT_MAPME_OP_NONE) + { + return (clib_error_return + (0, "Please specify valid operation, add or delete")); + } + return (ret == HICN_ERROR_NONE) ? clib_error_return (0, "Punting %s", + get_error_string (ret)) + : clib_error_return (0, get_error_string (ret)); +} + +/* + * cli handler for 'pgen' + */ +static clib_error_t * +hicn_cli_pgen_client_set_command_fn (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + hicn_main_t *sm = &hicn_main; + hicnpg_main_t *hpgm = &hicnpg_main; + ip46_address_t src_addr, hicn_name; + vnet_main_t *vnm = vnet_get_main (); + u32 sw_if_index = ~0; + u16 lifetime = 4000; + int rv = VNET_API_ERROR_UNIMPLEMENTED; + u32 max_seq = ~0; + u32 n_flows = ~0; + u32 mask = 0; + u32 n_ifaces = 1; + u32 hicn_underneath = ~0; + + /* Get a line of input. */ + unformat_input_t _line_input, *line_input = &_line_input; + if (unformat_user (main_input, unformat_line_input, line_input)) + { + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "fwd")) + { + if (unformat (line_input, "ip")) + hicn_underneath = 0; + else if (unformat (line_input, "hicn")) + hicn_underneath = 1; + } + if (unformat + (line_input, "intfc %U", unformat_vnet_sw_interface, vnm, + &sw_if_index)) + { + ; + } + else if (unformat (line_input, "src %U", + unformat_ip46_address, &src_addr)) + { + ; + } + else if (unformat (line_input, "n_ifaces %d", &n_ifaces)) + { + ; + } + else if (unformat (line_input, "name %U/%d", + unformat_ip46_address, &hicn_name, IP46_TYPE_ANY, + &mask)) + { + ; + } + else if (unformat (line_input, "lifetime %d", &lifetime)) + { + ; + } + else if (unformat (line_input, "max_seq %d", &max_seq)) + { + ; + } + else if (unformat (line_input, "n_flows %d", &n_flows)) + { + ; + } + else + { + return (clib_error_return + (0, "Unknown input '%U'", format_unformat_error, + line_input)); + break; + } + } + } + hpgm->interest_lifetime = lifetime; + + if (sw_if_index == ~0) + { + return (clib_error_return (0, "Packet generator interface missing")); + } + if (hicn_underneath == ~0) + { + return (clib_error_return + (0, "Choose the underlying forwarder type ip|hicn")); + } + else if (hicn_underneath && !sm->is_enabled) + { + return (clib_error_return (0, "hICN not enabled in VPP")); + } + else if (!hicn_underneath && sm->is_enabled) + { + return (clib_error_return (0, "hICN enabled in VPP")); + } + + int skip = 1; + int base_offset = ETH_L2; + u8 use_current_data = HICN_CLASSIFY_NO_CURRENT_DATA_FLAG; + + if (hicn_cli_is_ip_interface (vm, vnm, sw_if_index) == IP) + { + skip = 0; + base_offset = NO_L2; + use_current_data = HICN_CLASSIFY_CURRENT_DATA_FLAG; + } + /* + * Register punting on src address generated by pg and data punting + * on the name + */ + if (ip46_address_is_ip4 (&src_addr) && ip46_address_is_ip4 (&hicn_name)) + { + /* Add data node to the vpp graph */ + u32 next_hit_node = vlib_node_add_next (vm, + hicn_punt_glb. + hicn_node_info.ip4_inacl_node_index, + hicn_pg_data_node.index); + + /* Add pgen_client node to the vpp graph */ + vlib_node_add_next (vm, + pg_input_node.index, hicn_pg_interest_node.index); + + /* Create the punting table if it does not exist */ + hicn_punt_add_vnettbl (&ipv4, &ipv4_src, mask, ~0, sw_if_index, + base_offset, use_current_data); + hicn_punt_add_vnettbl (&ipv4, &ipv4_dst, mask, + hicn_punt_glb.ip4_vnet_tbl_idx[sw_if_index][skip] + [HICN_PUNT_SRC][mask], sw_if_index, base_offset, + use_current_data); + + /* Add a session to the table */ + hicn_punt_add_vnetssn (&ipv4, &ipv4_src, + &hicn_name, mask, + next_hit_node, sw_if_index, base_offset); + + hicn_punt_add_vnetssn (&ipv4, &ipv4_src, + &hicn_name, mask, + next_hit_node, sw_if_index, base_offset); + + hicn_punt_enable_disable_vnet_ip4_table_on_intf (vm, sw_if_index, + OP_ENABLE); + + pg_node_t *pn; + pn = pg_get_node (hicn_pg_interest_node.index); + pn->unformat_edit = unformat_pg_ip4_header; + + } + else if (!ip46_address_is_ip4 (&src_addr) + && !ip46_address_is_ip4 (&hicn_name)) + { + /* Add node to the vpp graph */ + u32 next_hit_node = vlib_node_add_next (vm, + hicn_punt_glb.hicn_node_info. + ip6_inacl_node_index, + hicn_pg_data_node.index); + + /* Add pgen_client node to the vpp graph */ + vlib_node_add_next (vm, pg_input_node.index, + hicn_pg_interest_node.index); + + /* Create the punting table if it does not exist */ + hicn_punt_add_vnettbl (&ipv6, &ipv6_src, mask, ~0, sw_if_index, + base_offset, use_current_data); + hicn_punt_add_vnettbl (&ipv6, &ipv6_dst, mask, + hicn_punt_glb.ip6_vnet_tbl_idx[sw_if_index][skip] + [HICN_PUNT_SRC][mask], sw_if_index, base_offset, + use_current_data); + + /* Add a session to the table */ + hicn_punt_add_vnetssn (&ipv6, &ipv6_src, + &hicn_name, mask, + next_hit_node, sw_if_index, base_offset); + + hicn_punt_add_vnetssn (&ipv6, &ipv6_src, + &hicn_name, mask, + next_hit_node, sw_if_index, base_offset); + + hicn_punt_enable_disable_vnet_ip6_table_on_intf (vm, sw_if_index, + OP_ENABLE); + + pg_node_t *pn; + pn = pg_get_node (hicn_pg_interest_node.index); + pn->unformat_edit = unformat_pg_ip6_header; + } + else + { + return (clib_error_return + (0, + "pg interface source address, source address and hicn name must be of the same type IPv4 or IPv6")); + } + + + hpgm->pgen_clt_src_addr = src_addr; + hpgm->pgen_clt_hicn_name = hicn_name; + hpgm->max_seq_number = max_seq; + hpgm->n_flows = n_flows; + hpgm->n_ifaces = n_ifaces; + hpgm->hicn_underneath = hicn_underneath; + vlib_cli_output (vm, "ifaces %d", hpgm->n_ifaces); + rv = 0; + + switch (rv) + { + case 0: + break; + + case VNET_API_ERROR_UNIMPLEMENTED: + return clib_error_return (0, "Unimplemented, NYI"); + break; + + default: + return clib_error_return (0, "hicn enable_disable returned %d", rv); + } + + return 0; +} + +/* + * cli handler for 'pgen' + */ +static clib_error_t * +hicn_cli_pgen_server_set_command_fn (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + clib_error_t *cl_err; + int rv = HICN_ERROR_NONE; + hicnpg_server_main_t *pg_main = &hicnpg_server_main; + hicn_main_t *sm = &hicn_main; + ip46_address_t hicn_name; + u32 subnet_mask; + int payload_size = 0; + u32 sw_if_index = ~0; + vnet_main_t *vnm = vnet_get_main (); + u32 hicn_underneath = ~0; + + /* Get a line of input. */ + unformat_input_t _line_input, *line_input = &_line_input; + if (unformat_user (main_input, unformat_line_input, line_input)) + { + /* Parse the arguments */ + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "fwd")) + { + if (unformat (line_input, "ip")) + hicn_underneath = 0; + else if (unformat (line_input, "hicn")) + hicn_underneath = 1; + } + if (unformat (line_input, "name %U/%d", + unformat_ip46_address, &hicn_name, IP46_TYPE_ANY, + &subnet_mask)) + {; + } + else if (unformat (line_input, "size %d", &payload_size)) + { + if (payload_size > 1440) + { + return (clib_error_return (0, + "Payload size must be <= 1440 bytes...")); + } + } + else + if (unformat + (line_input, "intfc %U", unformat_vnet_sw_interface, vnm, + &sw_if_index)) + { + ; + } + else + { + return (clib_error_return + (0, "Unknown input '%U'", format_unformat_error, + line_input)); + break; + } + } + } + /* Attach our packet-gen node for ip4 udp local traffic */ + if (payload_size == 0 || sw_if_index == ~0) + { + return clib_error_return (0, + "Error: must supply local port, payload size and incoming interface"); + } + if (hicn_underneath == ~0) + { + return (clib_error_return + (0, "Choose the underlying forwarder type ip|hicn")); + } + else if (hicn_underneath && !sm->is_enabled) + { + return (clib_error_return (0, "hICN not enabled in VPP")); + } + else if (!hicn_underneath && sm->is_enabled) + { + return (clib_error_return (0, "hICN enabled in VPP")); + } + pg_main->hicn_underneath = hicn_underneath; + + /* Allocate the buffer with the actual content payload TLV */ + vlib_buffer_alloc (vm, &pg_main->pgen_svr_buffer_idx, 1); + vlib_buffer_t *rb = NULL; + rb = vlib_get_buffer (vm, pg_main->pgen_svr_buffer_idx); + + /* Initialize the buffer data with zeros */ + memset (rb->data, 0, payload_size); + rb->current_length = payload_size; + + int skip = 2; + int base_offset = ETH_L2; + u8 use_current_data = HICN_CLASSIFY_NO_CURRENT_DATA_FLAG; + + if (hicn_cli_is_ip_interface (vm, vnm, sw_if_index) == IP) + { + skip = 1; + base_offset = NO_L2; + use_current_data = HICN_CLASSIFY_CURRENT_DATA_FLAG; + } + if (ip46_address_is_ip4 (&hicn_name)) + { + /* Add node to the vpp graph */ + u32 next_hit_node = vlib_node_add_next (vm, + hicn_punt_glb. + hicn_node_info.ip4_inacl_node_index, + hicn_pg_server_node.index); + + /* Create the punting table if it does not exist */ + hicn_punt_add_vnettbl (&ipv4, &ipv4_src, subnet_mask, ~0, sw_if_index, + base_offset, use_current_data); + hicn_punt_add_vnettbl (&ipv4, &ipv4_dst, subnet_mask, + hicn_punt_glb.ip4_vnet_tbl_idx[sw_if_index][skip] + [HICN_PUNT_SRC][subnet_mask - 1], sw_if_index, + base_offset, use_current_data); + + + /* Add a session to the table */ + hicn_punt_add_vnetssn (&ipv4, &ipv4_dst, + (ip46_address_t *) & (hicn_name.ip4), + subnet_mask, next_hit_node, sw_if_index, + base_offset); + + hicn_punt_enable_disable_vnet_ip4_table_on_intf (vm, sw_if_index, + OP_ENABLE); + + } + else + { + /* Add node to the vpp graph */ + u32 next_hit_node = vlib_node_add_next (vm, + hicn_punt_glb.hicn_node_info. + ip6_inacl_node_index, + hicn_pg_server_node.index); + + /* Create the punting table if it does not exist */ + hicn_punt_add_vnettbl (&ipv6, &ipv6_src, subnet_mask, ~0, sw_if_index, + base_offset, use_current_data); + hicn_punt_add_vnettbl (&ipv6, &ipv6_dst, subnet_mask, + hicn_punt_glb.ip6_vnet_tbl_idx[sw_if_index][skip] + [HICN_PUNT_SRC][subnet_mask - 1], sw_if_index, + base_offset, use_current_data); + + + /* Add a session to the table */ + hicn_punt_add_vnetssn (&ipv6, &ipv6_dst, + (ip46_address_t *) & (hicn_name.ip6), + subnet_mask, next_hit_node, sw_if_index, + base_offset); + + hicn_punt_enable_disable_vnet_ip6_table_on_intf (vm, sw_if_index, + OP_ENABLE); + } + + switch (rv) + { + case 0: + cl_err = 0; + break; + + case VNET_API_ERROR_UNIMPLEMENTED: + cl_err = clib_error_return (0, "Unimplemented, NYI"); + break; + + default: + cl_err = clib_error_return (0, "hicn pgen server returned %d", rv); + } + + return cl_err; +} + +/* cli declaration for 'control start' */ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND(hicn_cli_node_ctl_start_set_command, static)= +{ + .path = "hicn control start", + .short_help = "hicn control start", + .function = hicn_cli_node_ctl_start_set_command_fn, +}; + + +/* cli declaration for 'control stop' */ +VLIB_CLI_COMMAND(hicn_cli_node_ctl_stop_set_command, static)= +{ + .path = "hicn control stop", + .short_help = "hicn control stop", + .function = hicn_cli_node_ctl_stop_set_command_fn, +}; + + +/* cli declaration for 'control param' */ +VLIB_CLI_COMMAND(hicn_cli_node_ctl_param_set_command, static)= +{ + .path = "hicn control param", + .short_help = "hicn control param { pit { size | { dfltlife | minlife | maxlife } } | fib size | cs {size | app } }\n", + .function = hicn_cli_node_ctl_param_set_command_fn, +}; + +/* cli declaration for 'control' (root path of multiple commands, for help) */ +VLIB_CLI_COMMAND(hicn_cli_node_ctl_command, static)= +{ + .path = "hicn control", + .short_help = "hicn control" +}; + +/* cli declaration for 'fib' */ +VLIB_CLI_COMMAND(hicn_cli_fib_set_command, static)= +{ + .path = "hicn fib", + .short_help = "hicn fib {{add | delete } prefix face }" + " | set strategy prefix ", + .function = hicn_cli_fib_set_command_fn, +}; + +/* cli declaration for 'show' */ +VLIB_CLI_COMMAND(hicn_cli_show_command, static)= +{ + .path = "hicn show", + .short_help = "hicn show " + "[detail] [internal]" + "[strategies]", + .function = hicn_cli_show_command_fn, +}; + +/* cli declaration for 'punting' */ +VLIB_CLI_COMMAND(hicn_cli_punting_command, static)= +{ + .path = "hicn punting", + .short_help = "hicn punting {add|delete} prefix intfc type ", + .function = hicn_cli_punting_command_fn, +}; + +VLIB_CLI_COMMAND(hicn_cli_mapme_command, static)= +{ + .path = "hicn mapme", + .short_help = "hicn mapme {enable|disable|set }", + .function = hicn_cli_mapme_command_fn, +}; + +/* cli declaration for 'hicn pgen client' */ +VLIB_CLI_COMMAND(hicn_cli_pgen_client_set_command, static)= +{ + .path = "hicn pgen client", + .short_help = "hicn pgen client fwd src n_ifaces name lifetime intfc max_seq n_flows ", + .long_help = "Run hicn in packet-gen client mode\n", + .function = hicn_cli_pgen_client_set_command_fn, +}; + +/* cli declaration for 'hicn pgen client' */ +VLIB_CLI_COMMAND(hicn_cli_pgen_server_set_command, static)= +{ + .path = "hicn pgen server", + .short_help = "hicn pgen server fwd name intfc size ", + .long_help = "Run hicn in packet-gen server mode\n", + .function = hicn_cli_pgen_server_set_command_fn, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/data_fwd.h b/hicn-plugin/src/data_fwd.h new file mode 100755 index 000000000..7390382ef --- /dev/null +++ b/hicn-plugin/src/data_fwd.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_DATA_FWD_H__ +#define __HICN_DATA_FWD_H__ + +#include + +#include "pcs.h" + +/* + * Node context data; we think this is per-thread/instance + */ +typedef struct hicn_data_fwd_runtime_s +{ + vlib_combined_counter_main_t repm_counters; + + /* per-cpu vector of cloned packets */ + u32 **clones; +} hicn_data_fwd_runtime_t; + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; + u8 packet_data[64]; +} hicn_data_fwd_trace_t; + +typedef enum +{ + HICN_DATA_FWD_NEXT_V4_LOOKUP, + HICN_DATA_FWD_NEXT_V6_LOOKUP, + HICN_DATA_FWD_NEXT_ERROR_DROP, + HICN_DATA_FWD_N_NEXT, +} hicn_data_fwd_next_t; + +/** + *@brief Create a maximum of 256 clones of buffer and store them + * in the supplied array. Unlike the original function in the vlib + * library, we don't prevent cloning if n_buffer==1 and if + * s->current_length <= head_end_offset + CLIB_CACHE_LINE_BYTES * 2. + * + * @param vm - (vlib_main_t *) vlib main data structure pointer + * @param src_buffer - (u32) source buffer index + * @param buffers - (u32 * ) buffer index array + * @param n_buffers - (u16) number of buffer clones requested (<=256) + * @param head_end_offset - (u16) offset relative to current position + * where packet head ends + * @return - (u16) number of buffers actually cloned, may be + * less than the number requested or zero + */ +always_inline u16 +vlib_buffer_clone_256_2 (vlib_main_t * vm, u32 src_buffer, u32 * buffers, + u16 n_buffers, u16 head_end_offset) +{ + u16 i; + vlib_buffer_t *s = vlib_get_buffer (vm, src_buffer); + + ASSERT (n_buffers); + ASSERT (n_buffers <= 256); + + if (s->current_length <= CLIB_CACHE_LINE_BYTES * 2) + { + for (i = 0; i < n_buffers; i++) + { + vlib_buffer_t *d; + d = vlib_buffer_copy (vm, s); + clib_memcpy (d->opaque2, s->opaque2, sizeof (s->opaque2)); + if (d == 0) + return i; + buffers[i] = vlib_get_buffer_index (vm, d); + } + s->current_data += head_end_offset; + s->current_length -= head_end_offset; + return n_buffers; + } + n_buffers = vlib_buffer_alloc_from_free_list (vm, buffers, n_buffers, + vlib_buffer_get_free_list_index + (s)); + + for (i = 0; i < n_buffers; i++) + { + vlib_buffer_t *d = vlib_get_buffer (vm, buffers[i]); + d->current_data = s->current_data; + d->current_length = head_end_offset; + d->trace_index = s->trace_index; + vlib_buffer_set_free_list_index (d, + vlib_buffer_get_free_list_index (s)); + + d->total_length_not_including_first_buffer = s->current_length - + head_end_offset; + if (PREDICT_FALSE (s->flags & VLIB_BUFFER_NEXT_PRESENT)) + { + d->total_length_not_including_first_buffer += + s->total_length_not_including_first_buffer; + } + d->flags = s->flags | VLIB_BUFFER_NEXT_PRESENT; + d->flags &= ~VLIB_BUFFER_EXT_HDR_VALID; + clib_memcpy (d->opaque, s->opaque, sizeof (s->opaque)); + clib_memcpy (d->opaque2, s->opaque2, sizeof (s->opaque2)); + clib_memcpy (vlib_buffer_get_current (d), vlib_buffer_get_current (s), + head_end_offset); + d->next_buffer = src_buffer; + } + vlib_buffer_advance (s, head_end_offset); + s->n_add_refs = n_buffers - 1; + while (s->flags & VLIB_BUFFER_NEXT_PRESENT) + { + s = vlib_get_buffer (vm, s->next_buffer); + s->n_add_refs = n_buffers - 1; + } + + return n_buffers; +} + +/** + * @brief Create multiple clones of buffer and store them + * in the supplied array. Unlike the function in the vlib library, + * we allow src_buffer to have n_add_refs != 0. + * + * @param vm - (vlib_main_t *) vlib main data structure pointer + * @param src_buffer - (u32) source buffer index + * @param buffers - (u32 * ) buffer index array + * @param n_buffers - (u16) number of buffer clones requested (<=256) + * @param head_end_offset - (u16) offset relative to current position + * where packet head ends + * @return - (u16) number of buffers actually cloned, may be + * less than the number requested or zero + */ +always_inline u16 +vlib_buffer_clone2 (vlib_main_t * vm, u32 src_buffer, u32 * buffers, + u16 n_buffers, u16 head_end_offset) +{ + ASSERT (head_end_offset >= VLIB_BUFFER_MIN_CHAIN_SEG_SIZE); + + vlib_buffer_t *s = vlib_get_buffer (vm, src_buffer); + + /* + * total_length_not_including_first_buffer is not initialized to 0 + * when a buffer is used. + */ + if (PREDICT_TRUE (s->next_buffer == 0)) + s->total_length_not_including_first_buffer = 0; + + u16 n_cloned = 0; + u8 n_clone_src = 255 - s->n_add_refs; + + /* + * We need to copy src for all the clones that cannot be chained in + * the src_buffer + */ + /* MAX(n_add_refs) = 256 */ + if (n_buffers > n_clone_src) + { + vlib_buffer_t *copy; + /* Ok to call the original vlib_buffer_copy. */ + copy = vlib_buffer_copy (vm, s); + n_cloned += vlib_buffer_clone (vm, + vlib_get_buffer_index (vm, copy), + buffers, + n_buffers - n_clone_src, + head_end_offset); + n_buffers -= n_cloned; + } + /* + * vlib_buffer_clone_256 check if n_add_refs is 0. We force it to be + * 0 before calling the function and we retore it to the right value + * after the function has been called + */ + u8 tmp_n_add_refs = s->n_add_refs; + + s->n_add_refs = 0; + /* + * The regular vlib_buffer_clone_256 does copy if we need to clone + * only one packet. While this is not a problem per se, it adds + * complexity to the code, especially because we need to add 1 to + * n_add_refs when the packet is cloned. + */ + n_cloned += vlib_buffer_clone_256_2 (vm, + src_buffer, + (buffers + n_cloned), + n_buffers, head_end_offset); + + s->n_add_refs += tmp_n_add_refs; + + return n_cloned; +} + +#endif /* //__HICN_DATA_FWD_H__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/data_fwd_node.c b/hicn-plugin/src/data_fwd_node.c new file mode 100755 index 000000000..088683fe0 --- /dev/null +++ b/hicn-plugin/src/data_fwd_node.c @@ -0,0 +1,541 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "data_fwd.h" +#include "mgmt.h" +#include "parser.h" +#include "infra.h" +#include "strategy.h" +#include "strategy_dpo_manager.h" +#include "state.h" +#include "error.h" + +/* Stats string values */ +static char *hicn_data_fwd_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + +/* Declarations */ +always_inline void +drop_packet (vlib_main_t * vm, u32 bi0, + u32 * n_left_to_next, u32 * next0, u32 ** to_next, + u32 * next_index, vlib_node_runtime_t * node); + +always_inline int +hicn_satisfy_faces (vlib_main_t * vm, u32 b0, + hicn_pcs_entry_t * pitp, u32 * n_left_to_next, + u32 ** to_next, u32 * next_index, + vlib_node_runtime_t * node, u8 isv6, + vl_api_hicn_api_node_stats_get_reply_t * stats); + +always_inline void +clone_data_to_cs (vlib_main_t * vm, hicn_pit_cs_t * pitcs, + hicn_pcs_entry_t * pitp, hicn_header_t * hicn0, f64 tnow, + hicn_hash_node_t * nodep, vlib_buffer_t * b0, + hicn_hash_entry_t * hash_entry, u64 name_hash, + hicn_buffer_t * hicnb, const hicn_dpo_vft_t * dpo_vft, + dpo_id_t * hicn_dpo_id); + + +/* packet trace format function */ +always_inline u8 *hicn_data_fwd_format_trace (u8 * s, va_list * args); + +vlib_node_registration_t hicn_data_fwd_node; + +/* + * ICN forwarder node for interests: handling of Data delivered based on ACL. + * - 1 packet at a time - ipv4/tcp ipv6/tcp + */ +static uword +hicn_data_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + + u32 n_left_from, *from, *to_next; + hicn_data_fwd_next_t next_index; + hicn_pit_cs_t *pitcs = &hicn_main.pitcs; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + f64 tnow; + u32 data_received = 1; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + /* Capture time in vpp terms */ + tnow = vlib_time_now (vm); + + while (n_left_from > 0) + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + vlib_buffer_t *b0; + u8 isv6; + u8 *nameptr; + u16 namelen; + u32 bi0; + u32 next0 = HICN_DATA_FWD_NEXT_ERROR_DROP; + hicn_name_t name; + hicn_header_t *hicn0; + hicn_buffer_t *hicnb0; + hicn_hash_node_t *node0; + const hicn_strategy_vft_t *strategy_vft0; + const hicn_dpo_vft_t *dpo_vft0; + u8 dpo_ctx_id0; + hicn_pcs_entry_t *pitp; + hicn_hash_entry_t *hash_entry0; + int ret = HICN_ERROR_NONE; + + /* Prefetch for next iteration. */ + if (n_left_from > 1) + { + vlib_buffer_t *b1; + b1 = vlib_get_buffer (vm, from[1]); + CLIB_PREFETCH (b1, 2 * CLIB_CACHE_LINE_BYTES, STORE); + CLIB_PREFETCH (b1->data, CLIB_CACHE_LINE_BYTES, STORE); + + /* HICN PREFETCH */ + hicn_buffer_t *hicnb1 = hicn_get_buffer (b1); + hicn_prefetch_pcs_entry (hicnb1, pitcs); + } + /* Dequeue a packet buffer */ + /* + * Do not copy the index in the next buffer, we'll do + * it later. The packet might be cloned, so the buffer to move + * to next must be the cloned one + */ + bi0 = from[0]; + from += 1; + n_left_from -= 1; + + b0 = vlib_get_buffer (vm, bi0); + + /* Get hicn buffer and state */ + hicnb0 = hicn_get_buffer (b0); + hicn_get_internal_state (hicnb0, pitcs, &node0, &strategy_vft0, + &dpo_vft0, &dpo_ctx_id0, &hash_entry0); + + ret = hicn_data_parse_pkt (b0, &name, &namelen, &hicn0, &isv6); + pitp = hicn_pit_get_data (node0); + nameptr = (u8 *) (&name); + + if (PREDICT_FALSE + (ret != HICN_ERROR_NONE + || !hicn_node_compare (nameptr, namelen, node0) + || (hash_entry0->he_flags & HICN_HASH_ENTRY_FLAG_CS_ENTRY))) + { + /* + * Remove the lock acquired from + * data_pcslookup node + */ + dpo_id_t hicn_dpo_id0 = { dpo_vft0->hicn_dpo_get_type (), 0, 0, + dpo_ctx_id0 + }; + hicn_pcs_remove_lock (pitcs, &pitp, &node0, vm, + hash_entry0, dpo_vft0, &hicn_dpo_id0); + + drop_packet (vm, bi0, &n_left_to_next, &next0, &to_next, + &next_index, node); + + goto end_processing; + } + /* + * Check if the hit is instead a collision in the + * hash table. Unlikely to happen. + */ + /* + * there is no guarantee that the type of entry has + * not changed from the lookup. + */ + + if (tnow > pitp->shared.expire_time) + { + dpo_id_t hicn_dpo_id0 = + { dpo_vft0->hicn_dpo_get_type (), 0, 0, dpo_ctx_id0 }; + hicn_pcs_delete (pitcs, &pitp, &node0, vm, hash_entry0, + dpo_vft0, &hicn_dpo_id0); + + drop_packet (vm, bi0, &n_left_to_next, &next0, &to_next, + &next_index, node); + stats.pit_expired_count++; + } + else + { + ASSERT ((hash_entry0->he_flags & HICN_HASH_ENTRY_FLAG_DELETED) + == 0); + + data_received++; + /* + * We do not check if the data is coming from + * the outgoing interest face. + */ + + /* Prepare the buffer for the cloning */ + ret = hicn_satisfy_faces (vm, bi0, pitp, &n_left_to_next, + &to_next, &next_index, node, + isv6, &stats); + + dpo_id_t hicn_dpo_id0 = { dpo_vft0->hicn_dpo_get_type (), 0, 0, + dpo_ctx_id0 + }; + + if (PREDICT_FALSE (ret != HICN_ERROR_NONE)) + { + hicn_pcs_pit_delete (pitcs, &pitp, &node0, vm, + hash_entry0, dpo_vft0, &hicn_dpo_id0); + continue; + } + /* + * Call the strategy callback since the + * interest has been satisfied + */ + strategy_vft0->hicn_receive_data (dpo_ctx_id0, + pitp->u.pit.pe_txnh); + +#if HICN_FEATURE_CS + /* + * Clone data packet in the content store and + * convert the PIT entry into a CS entry + */ + clone_data_to_cs (vm, pitcs, pitp, hicn0, tnow, node0, + b0, hash_entry0, hicnb0->name_hash, hicnb0, + dpo_vft0, &hicn_dpo_id0); + + hicn_pcs_remove_lock (pitcs, &pitp, &node0, vm, + hash_entry0, NULL, NULL); +#else + ASSERT (pitp == hicn_pit_get_data (node0)); + /* + * Remove one reference as the buffer is no + * longer in any frame + */ + b0->n_add_refs--; + /* If not enabled, delete the PIT entry */ + hicn_pcs_pit_delete (pitcs, &pitp, &node0, vm, + hash_entry0, dpo_vft0, &hicn_dpo_id0); +#endif + } + end_processing: + + /* Incr packet counter */ + stats.pkts_processed += 1; + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + u32 pit_int_count = hicn_pit_get_int_count (pitcs); + u32 pit_cs_count = hicn_pit_get_cs_count (pitcs); + + vlib_node_increment_counter (vm, hicn_data_fwd_node.index, + HICNFWD_ERROR_DATAS, stats.pkts_data_count); + + + update_node_counter (vm, hicn_data_fwd_node.index, + HICNFWD_ERROR_INT_COUNT, pit_int_count); + update_node_counter (vm, hicn_data_fwd_node.index, + HICNFWD_ERROR_CS_COUNT, pit_cs_count); + update_node_counter (vm, hicn_data_fwd_node.index, + HICNFWD_ERROR_INTEREST_AGG_ENTRY, + stats.pkts_data_count / data_received); + + return (frame->n_vectors); +} + +always_inline void +drop_packet (vlib_main_t * vm, u32 bi0, + u32 * n_left_to_next, u32 * next0, u32 ** to_next, + u32 * next_index, vlib_node_runtime_t * node) +{ + *next0 = HICN_DATA_FWD_NEXT_ERROR_DROP; + + (*to_next)[0] = bi0; + *to_next += 1; + *n_left_to_next -= 1; + + vlib_validate_buffer_enqueue_x1 (vm, node, *next_index, + *to_next, *n_left_to_next, bi0, *next0); +} + +always_inline int +hicn_satisfy_faces (vlib_main_t * vm, u32 bi0, + hicn_pcs_entry_t * pitp, u32 * n_left_to_next, + u32 ** to_next, u32 * next_index, + vlib_node_runtime_t * node, u8 isv6, + vl_api_hicn_api_node_stats_get_reply_t * stats) +{ + int found = 0; + int ret = HICN_ERROR_NONE; + u32 *clones = NULL, *header = NULL; + u32 n_left_from = 0; + u32 next0 = HICN_DATA_FWD_NEXT_ERROR_DROP, next1 = + HICN_DATA_FWD_NEXT_ERROR_DROP; + + /* + * We have a hard limit on the number of vlib_buffer that we can + * chain (no more than 256) + */ + /* + * The first group of vlib_buffer can be directly cloned from b0. We + * need to be careful to clone it only 254 times as the buffer + * already has n_add_reds=1. + */ + vec_alloc (clones, pitp->u.pit.faces.n_faces); + header = clones; + + /* Clone bi0 */ + vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0); + + /* Add one reference to maintain the buffer in the CS */ + b0->n_add_refs++; + found = n_left_from = + vlib_buffer_clone2 (vm, bi0, clones, pitp->u.pit.faces.n_faces, + VLIB_BUFFER_MIN_CHAIN_SEG_SIZE); + + ASSERT (n_left_from == pitp->u.pit.faces.n_faces); + + /* Index to iterate over the faces */ + int i = 0; + + while (n_left_from > 0) + { + + //Dual loop, X2 + while (n_left_from >= 4 && *n_left_to_next >= 2) + { + vlib_buffer_t *h0, *h1; + u32 hi0, hi1; + dpo_id_t *face0, *face1; + + /* Prefetch for next iteration. */ + { + vlib_buffer_t *h2, *h3; + h2 = vlib_get_buffer (vm, clones[2]); + h3 = vlib_get_buffer (vm, clones[3]); + CLIB_PREFETCH (h2, 2 * CLIB_CACHE_LINE_BYTES, STORE); + CLIB_PREFETCH (h3, 2 * CLIB_CACHE_LINE_BYTES, STORE); + } + + face0 = hicn_face_db_get_dpo_face (i++, &pitp->u.pit.faces); + face1 = hicn_face_db_get_dpo_face (i++, &pitp->u.pit.faces); + + h0 = vlib_get_buffer (vm, clones[0]); + h1 = vlib_get_buffer (vm, clones[1]); + + (*to_next)[0] = hi0 = clones[0]; + (*to_next)[1] = hi1 = clones[1]; + *to_next += 2; + *n_left_to_next -= 2; + n_left_from -= 2; + clones += 2; + + next0 = face0->dpoi_next_node; + next1 = face1->dpoi_next_node; + vnet_buffer (h0)->ip.adj_index[VLIB_TX] = face0->dpoi_index; + vnet_buffer (h1)->ip.adj_index[VLIB_TX] = face1->dpoi_index; + + stats->pkts_data_count += 2; + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (h0->flags & VLIB_BUFFER_IS_TRACED))) + { + hicn_data_fwd_trace_t *t = + vlib_add_trace (vm, node, h0, sizeof (*t)); + t->pkt_type = HICN_PKT_TYPE_CONTENT; + t->sw_if_index = vnet_buffer (h0)->sw_if_index[VLIB_RX]; + t->next_index = next0; + clib_memcpy (t->packet_data, + vlib_buffer_get_current (h0), + sizeof (t->packet_data)); + } + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (h1->flags & VLIB_BUFFER_IS_TRACED))) + { + hicn_data_fwd_trace_t *t = + vlib_add_trace (vm, node, h1, sizeof (*t)); + t->pkt_type = HICN_PKT_TYPE_CONTENT; + t->sw_if_index = vnet_buffer (h1)->sw_if_index[VLIB_RX]; + t->next_index = next1; + clib_memcpy (t->packet_data, + vlib_buffer_get_current (h1), + sizeof (t->packet_data)); + } + vlib_validate_buffer_enqueue_x2 (vm, node, *next_index, + *to_next, *n_left_to_next, + hi0, hi1, next0, next1); + } + + + while (n_left_from > 0 && *n_left_to_next > 0) + { + vlib_buffer_t *h0; + u32 hi0; + dpo_id_t *face0; + + face0 = hicn_face_db_get_dpo_face (i++, &pitp->u.pit.faces); + + h0 = vlib_get_buffer (vm, clones[0]); + + (*to_next)[0] = hi0 = clones[0]; + *to_next += 1; + *n_left_to_next -= 1; + n_left_from -= 1; + clones += 1; + next0 = face0->dpoi_next_node; + vnet_buffer (h0)->ip.adj_index[VLIB_TX] = face0->dpoi_index; + + stats->pkts_data_count++; + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (h0->flags & VLIB_BUFFER_IS_TRACED))) + { + hicn_data_fwd_trace_t *t = + vlib_add_trace (vm, node, h0, sizeof (*t)); + t->pkt_type = HICN_PKT_TYPE_CONTENT; + t->sw_if_index = vnet_buffer (h0)->sw_if_index[VLIB_RX]; + t->next_index = next0; + clib_memcpy (t->packet_data, + vlib_buffer_get_current (h0), + sizeof (t->packet_data)); + } + /* + * Verify speculative enqueue, maybe switch current + * next frame + */ + /* + * Fix in case of a wrong speculation. Needed to + * clone the data in the right frame + */ + vlib_validate_buffer_enqueue_x1 (vm, node, *next_index, + *to_next, *n_left_to_next, + hi0, next0); + + } + + /* Ensure that there is space for the next clone (if any) */ + if (PREDICT_FALSE (*n_left_to_next == 0)) + { + vlib_put_next_frame (vm, node, *next_index, *n_left_to_next); + + vlib_get_next_frame (vm, node, *next_index, *to_next, + *n_left_to_next); + } + } + + + vec_free (header); + + if (PREDICT_FALSE (!found)) + { + ASSERT (0); + drop_packet (vm, bi0, n_left_to_next, &next0, to_next, next_index, + node); + ret = HICN_ERROR_FACE_NOT_FOUND; + } + return ret; +} + +always_inline void +clone_data_to_cs (vlib_main_t * vm, hicn_pit_cs_t * pitcs, + hicn_pcs_entry_t * pitp, hicn_header_t * hicn0, f64 tnow, + hicn_hash_node_t * nodep, vlib_buffer_t * b0, + hicn_hash_entry_t * hash_entry, u64 name_hash, + hicn_buffer_t * hicnb, const hicn_dpo_vft_t * dpo_vft, + dpo_id_t * hicn_dpo_id) +{ + hicn_lifetime_t dmsg_lifetime; + /* + * At this point we think we're safe to proceed. Store the CS buf in + * the PIT/CS hashtable entry + */ + + /* + * Start turning the PIT into a CS. Note that we may be stepping on + * the PIT part of the union as we update the CS part, so don't + * expect the PIT part to be valid after this point. + */ + hicn_buffer_t *hicnb0 = hicn_get_buffer (b0); + hicn_pit_to_cs (vm, pitcs, pitp, hash_entry, nodep, dpo_vft, hicn_dpo_id, + &hicnb->face_dpo_id, hicnb0->is_appface); + + pitp->shared.create_time = tnow; + + hicn_type_t type = hicnb0->type; + hicn_ops_vft[type.l1]->get_lifetime (type, &hicn0->protocol, + &dmsg_lifetime); + + if (dmsg_lifetime < HICN_PARAM_CS_LIFETIME_MIN + || dmsg_lifetime > HICN_PARAM_CS_LIFETIME_MAX) + { + dmsg_lifetime = HICN_PARAM_CS_LIFETIME_DFLT; + } + pitp->shared.expire_time = hicn_pcs_get_exp_time (tnow, dmsg_lifetime); + + /* Store the original packet buffer in the CS node */ + pitp->u.cs.cs_pkt_buf = vlib_get_buffer_index (vm, b0); +} + +/* packet trace format function */ +always_inline u8 * +hicn_data_fwd_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_data_fwd_trace_t *t = va_arg (*args, hicn_data_fwd_trace_t *); + u32 indent = format_get_indent (s); + + s = format (s, "DATAFWD: pkt: %d, sw_if_index %d, next index %d\n", + (int) t->pkt_type, t->sw_if_index, t->next_index); + + s = format (s, "%U%U", format_white_space, indent, + format_ip6_header, t->packet_data, sizeof (t->packet_data)); + return (s); +} + +/* + * Node registration for the data forwarder node + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE(hicn_data_fwd_node) = +{ + .function = hicn_data_node_fn, + .name = "hicn-data-fwd", + .vector_size = sizeof(u32), + .runtime_data_bytes = sizeof(hicn_data_fwd_runtime_t), + .format_trace = hicn_data_fwd_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(hicn_data_fwd_error_strings), + .error_strings = hicn_data_fwd_error_strings, + .n_next_nodes = HICN_DATA_FWD_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = { + [HICN_DATA_FWD_NEXT_V4_LOOKUP] = "ip4-lookup", + [HICN_DATA_FWD_NEXT_V6_LOOKUP] = "ip6-lookup", + [HICN_DATA_FWD_NEXT_ERROR_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/data_pcslookup.h b/hicn-plugin/src/data_pcslookup.h new file mode 100755 index 000000000..fa75c3ac3 --- /dev/null +++ b/hicn-plugin/src/data_pcslookup.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_DATA_PCSLOOKUP_H__ +#define __HICN_DATA_PCSLOOKUP_H__ + +#include "pcs.h" + +/* + * Node context data; we think this is per-thread/instance + */ +typedef struct hicn_data_pcslookup_runtime_s +{ + int id; + hicn_pit_cs_t *pitcs; +} hicn_data_pcslookup_runtime_t; + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; +} hicn_data_pcslookup_trace_t; + +typedef enum +{ + HICN_DATA_PCSLOOKUP_NEXT_V4_LOOKUP, + HICN_DATA_PCSLOOKUP_NEXT_V6_LOOKUP, + HICN_DATA_PCSLOOKUP_NEXT_STORE_DATA, + HICN_DATA_PCSLOOKUP_NEXT_DATA_FWD, /* This must be one position + * before the error drop!! */ + HICN_DATA_PCSLOOKUP_NEXT_ERROR_DROP, + HICN_DATA_PCSLOOKUP_N_NEXT, +} hicn_data_pcslookup_next_t; + +#endif /* //__HICN_DATA_PCSLOOKUP_H__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/data_pcslookup_node.c b/hicn-plugin/src/data_pcslookup_node.c new file mode 100755 index 000000000..222545106 --- /dev/null +++ b/hicn-plugin/src/data_pcslookup_node.c @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "data_pcslookup.h" +#include "mgmt.h" +#include "parser.h" +#include "infra.h" +#include "strategy.h" +#include "strategy_dpo_manager.h" +#include "state.h" + +/* Stats string values */ +static char *hicn_data_pcslookup_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + +/* packet trace format function */ +always_inline u8 *hicn_data_pcslookup_format_trace (u8 * s, va_list * args); + +vlib_node_registration_t hicn_data_pcslookup_node; + +/* + * hICN node for handling data. It performs a lookup in the PIT. + */ +static uword +hicn_data_pcslookup_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + + u32 n_left_from, *from, *to_next; + hicn_data_pcslookup_next_t next_index; + hicn_data_pcslookup_runtime_t *rt; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + + rt = vlib_node_get_runtime_data (vm, node->node_index); + + if (PREDICT_FALSE (rt->pitcs == NULL)) + { + rt->pitcs = &hicn_main.pitcs; + } + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + vlib_buffer_t *b0; + hicn_buffer_t *hb0; + u8 isv6; + u8 *nameptr; + u16 namelen; + u32 bi0; + u32 next0 = HICN_DATA_PCSLOOKUP_NEXT_ERROR_DROP; + u64 name_hash = 0; + hicn_name_t name; + hicn_header_t *hicn0; + u32 node_id0 = 0; + u8 dpo_ctx_id0 = 0; + int ret0; + u8 vft_id0; + u8 is_cs0; + u8 hash_entry_id = 0; + u8 bucket_is_overflown = 0; + u32 bucket_id = ~0; + + /* Prefetch for next iteration. */ + if (n_left_from > 1) + { + vlib_buffer_t *b1; + b1 = vlib_get_buffer (vm, from[1]); + //Prefetch one cache line-- 64 byte-- so that we load the hicn_buffer_t as well + CLIB_PREFETCH (b1, 2 * CLIB_CACHE_LINE_BYTES, STORE); + CLIB_PREFETCH (b1->data, CLIB_CACHE_LINE_BYTES, LOAD); + } + /* Dequeue a packet buffer */ + bi0 = from[0]; + from += 1; + n_left_from -= 1; + to_next[0] = bi0; + to_next += 1; + n_left_to_next -= 1; + + + b0 = vlib_get_buffer (vm, bi0); + hb0 = hicn_get_buffer (b0); + + /* Incr packet counter */ + stats.pkts_processed += 1; + + ret0 = hicn_data_parse_pkt (b0, &name, &namelen, &hicn0, &isv6); + + if (PREDICT_TRUE (ret0 == HICN_ERROR_NONE)) + { + next0 = + isv6 ? HICN_DATA_PCSLOOKUP_NEXT_V6_LOOKUP : + HICN_DATA_PCSLOOKUP_NEXT_V4_LOOKUP; + } + nameptr = (u8 *) (&name); + if (PREDICT_FALSE + (ret0 != HICN_ERROR_NONE + || hicn_hashtb_fullhash (nameptr, namelen, + &name_hash) != HICN_ERROR_NONE)) + { + next0 = HICN_DATA_PCSLOOKUP_NEXT_ERROR_DROP; + } + else + { + int res = + hicn_hashtb_lookup_node (rt->pitcs->pcs_table, nameptr, + namelen, name_hash, + !(hb0->is_appface) /* take lock */ , + &node_id0, &dpo_ctx_id0, &vft_id0, + &is_cs0, + &hash_entry_id, &bucket_id, + &bucket_is_overflown); + + stats.pkts_data_count += 1; + + if ((res == HICN_ERROR_HASHTB_HASH_NOT_FOUND + || (res == HICN_ERROR_NONE && is_cs0)) + && (hb0->is_appface)) + { + next0 = HICN_DATA_PCSLOOKUP_NEXT_STORE_DATA; + } + else if (res == HICN_ERROR_NONE) + { + /* + * In case the result of the lookup + * is a CS entry, the packet is + * dropped + */ + next0 = HICN_DATA_PCSLOOKUP_NEXT_DATA_FWD + is_cs0; + } + } + + hicn_store_internal_state (b0, name_hash, node_id0, dpo_ctx_id0, + vft_id0, hash_entry_id, bucket_id, + bucket_is_overflown); + + /* + * Verify speculative enqueue, maybe switch current + * next frame + */ + /* + * Fix in case of a wrong speculation. Needed to + * clone the data in the right frame + */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + + /* Maybe trace */ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + hicn_data_pcslookup_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->pkt_type = HICN_PKT_TYPE_CONTENT; + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + t->next_index = next0; + } + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + /* Check the CS LRU, and trim if necessary. */ + u32 pit_int_count = hicn_pit_get_int_count (rt->pitcs); + u32 pit_cs_count = hicn_pit_get_cs_count (rt->pitcs); + + vlib_node_increment_counter (vm, hicn_data_pcslookup_node.index, + HICNFWD_ERROR_PROCESSED, stats.pkts_processed); + + vlib_node_increment_counter (vm, hicn_data_pcslookup_node.index, + HICNFWD_ERROR_DATAS, stats.pkts_data_count); + + update_node_counter (vm, hicn_data_pcslookup_node.index, + HICNFWD_ERROR_INT_COUNT, pit_int_count); + update_node_counter (vm, hicn_data_pcslookup_node.index, + HICNFWD_ERROR_CS_COUNT, pit_cs_count); + return (frame->n_vectors); +} + +/* packet trace format function */ +static u8 * +hicn_data_pcslookup_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_data_pcslookup_trace_t *t = + va_arg (*args, hicn_data_pcslookup_trace_t *); + + s = format (s, "DATA-PCSLOOKUP: pkt: %d, sw_if_index %d, next index %d", + (int) t->pkt_type, t->sw_if_index, t->next_index); + return (s); +} + + +/* + * Node registration for the data forwarder node + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (hicn_data_pcslookup_node) = +{ + .function = hicn_data_pcslookup_node_fn, + .name = "hicn-data-pcslookup", + .vector_size = sizeof (u32), + .runtime_data_bytes = sizeof (hicn_data_pcslookup_runtime_t), + .format_trace = hicn_data_pcslookup_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (hicn_data_pcslookup_error_strings), + .error_strings = hicn_data_pcslookup_error_strings, + .n_next_nodes = HICN_DATA_PCSLOOKUP_N_NEXT, + .next_nodes = + { + [HICN_DATA_PCSLOOKUP_NEXT_V4_LOOKUP] = "ip4-lookup", + [HICN_DATA_PCSLOOKUP_NEXT_V6_LOOKUP] = "ip6-lookup", + [HICN_DATA_PCSLOOKUP_NEXT_STORE_DATA] = "hicn-data-push", + [HICN_DATA_PCSLOOKUP_NEXT_DATA_FWD] = "hicn-data-fwd", + [HICN_DATA_PCSLOOKUP_NEXT_ERROR_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/data_push_node.c b/hicn-plugin/src/data_push_node.c new file mode 100755 index 000000000..a4a25e29b --- /dev/null +++ b/hicn-plugin/src/data_push_node.c @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "hicn.h" +#include "parser.h" +#include "strategy_dpo_ctx.h" +#include "infra.h" +#include "mgmt.h" +#include "pcs.h" + +/* + * Node context data (to be used in all the strategy nodes); we think this is + * per-thread/instance + */ +typedef struct hicn_data_push_runtime_s +{ + int id; + hicn_pit_cs_t *pitcs; +} hicn_data_push_runtime_t; + +/* Stats string values */ +static char *hicn_data_push_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + +typedef enum +{ + HICN_DATA_PUSH_NEXT_ERROR_DROP, + HICN_DATA_PUSH_N_NEXT, +} hicn_data_push_next_t; + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; + u8 packet_data[40]; +} hicn_data_push_trace_t; + +vlib_node_registration_t hicn_data_push_node; + +always_inline void +prep_buffer_for_cs (vlib_main_t * vm, vlib_buffer_t * b0, u8 isv6) +{ + if (isv6) + { + /* Advance the vlib buffer to the beginning of the TCP header */ + vlib_buffer_advance (b0, sizeof (ip6_header_t) + sizeof (tcp_header_t)); + b0->total_length_not_including_first_buffer = 0; + } + else + { + /* Advance the vlib buffer to the beginning of the TCP header */ + vlib_buffer_advance (b0, sizeof (ip4_header_t) + sizeof (tcp_header_t)); + b0->total_length_not_including_first_buffer = 0; + } +} + +always_inline int +hicn_new_data (vlib_main_t * vm, hicn_data_push_runtime_t * rt, + vlib_buffer_t * b0, u32 * next, f64 tnow, u8 * nameptr, + u16 namelen, u8 isv6) +{ + int ret; + hicn_hash_node_t *nodep; + hicn_pcs_entry_t *pitp; + hicn_header_t *hicn0; + hicn_buffer_t *hicnb0 = hicn_get_buffer (b0); + u32 node_id0 = 0; + u8 dpo_ctx_id0 = 0; + u8 vft_id0 = 0; + u8 is_cs0 = 0; + u8 hash_entry_id = 0; + u32 bucket_id = ~0; + u8 bucket_is_overflow = 0; + hicn_lifetime_t dmsg_lifetime; + + /* Create PIT node and init PIT entry */ + nodep = hicn_hashtb_alloc_node (rt->pitcs->pcs_table); + if (PREDICT_FALSE (nodep == NULL)) + { + /* Nothing we can do - no mem */ + *next = HICN_DATA_PUSH_NEXT_ERROR_DROP; + return HICN_ERROR_HASHTB_NOMEM; + } + pitp = hicn_pit_get_data (nodep); + hicn_pit_init_data (pitp); + pitp->shared.create_time = tnow; + + hicn0 = vlib_buffer_get_current (b0); + + hicn_type_t type = hicnb0->type; + hicn_ops_vft[type.l1]->get_lifetime (type, &hicn0->protocol, + &dmsg_lifetime); + + if (dmsg_lifetime < HICN_PARAM_CS_LIFETIME_MIN + || dmsg_lifetime > HICN_PARAM_CS_LIFETIME_MAX) + { + dmsg_lifetime = HICN_PARAM_CS_LIFETIME_DFLT; + } + pitp->shared.expire_time = hicn_pcs_get_exp_time (tnow, dmsg_lifetime); + prep_buffer_for_cs (vm, b0, isv6); + + /* Store the original packet buffer in the CS node */ + pitp->u.cs.cs_pkt_buf = vlib_get_buffer_index (vm, b0); + + pitp->u.cs.cs_rxface = hicnb0->face_dpo_id; + + /* Set up the hash node and insert it */ + hicn_hashtb_init_node (rt->pitcs->pcs_table, nodep, nameptr, namelen); + + + nodep->hn_flags |= HICN_HASH_NODE_CS_FLAGS; + pitp->shared.entry_flags |= HICN_PCS_ENTRY_CS_FLAG; + + hicn_hash_entry_t *hash_entry; + ret = + hicn_pcs_cs_insert_update (vm, rt->pitcs, pitp, nodep, &hash_entry, + hicnb0->name_hash, &node_id0, &dpo_ctx_id0, + &vft_id0, &is_cs0, &hash_entry_id, &bucket_id, + &bucket_is_overflow); + + hash_entry->he_flags |= HICN_HASH_ENTRY_FLAG_CS_ENTRY; + if (ret != HICN_ERROR_NONE) + { + hicn_hashtb_free_node (rt->pitcs->pcs_table, nodep); + } + return (ret); + +} + +/* + * ICN strategy later node for interests: - 1 packet at a time - ipv4/tcp + * ipv6/tcp + */ +uword +hicn_data_push_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + + u32 n_left_from, *from, *to_next, n_left_to_next; + hicn_data_push_next_t next_index; + hicn_data_push_runtime_t *rt; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + f64 tnow; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = (hicn_data_push_next_t) node->cached_next_index; + rt = vlib_node_get_runtime_data (vm, hicn_data_push_node.index); + rt->pitcs = &hicn_main.pitcs; + /* Capture time in vpp terms */ + tnow = vlib_time_now (vm); + + while (n_left_from > 0) + { + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + /* Dual loop, X2 */ + while (n_left_from >= 4 && n_left_to_next >= 2) + { + u8 isv6_0, isv6_1; + u8 *nameptr0, *nameptr1; + u16 namelen0, namelen1; + hicn_name_t name0, name1; + hicn_header_t *hicn0, *hicn1; + vlib_buffer_t *b0, *b1; + u32 bi0, bi1; + u32 next0 = next_index, next1 = next_index; + int ret0, ret1; + + /* Prefetch for next iteration. */ + { + vlib_buffer_t *b2, *b3; + b2 = vlib_get_buffer (vm, from[2]); + b3 = vlib_get_buffer (vm, from[3]); + CLIB_PREFETCH (b2, 2 * CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH (b3, 2 * CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH (b2->data, CLIB_CACHE_LINE_BYTES, STORE); + CLIB_PREFETCH (b3->data, CLIB_CACHE_LINE_BYTES, STORE); + } + + /* Dequeue a packet buffer */ + bi0 = from[0]; + bi1 = from[1]; + from += 2; + n_left_from -= 2; + /* to_next[0] = bi0; */ + /* to_next[1] = bi1; */ + /* to_next += 2; */ + /* n_left_to_next -= 2; */ + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + next0 = next1 = HICN_DATA_PUSH_NEXT_ERROR_DROP; + + ret0 = hicn_data_parse_pkt (b0, &name0, &namelen0, &hicn0, &isv6_0); + ret1 = hicn_data_parse_pkt (b1, &name1, &namelen1, &hicn1, &isv6_1); + + nameptr0 = (u8 *) (&name0); + nameptr1 = (u8 *) (&name1); + if (PREDICT_TRUE (ret0 == HICN_ERROR_NONE)) + hicn_new_data (vm, rt, b0, &next0, tnow, nameptr0, namelen0, + isv6_0); + + if (PREDICT_TRUE (ret1 == HICN_ERROR_NONE)) + hicn_new_data (vm, rt, b1, &next1, tnow, nameptr1, namelen1, + isv6_1); + stats.pkts_data_count += 2; + + /* Maybe trace */ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + hicn_data_push_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->pkt_type = HICN_PKT_TYPE_CONTENT; + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];; + t->next_index = next0; + } + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (b1->flags & VLIB_BUFFER_IS_TRACED))) + { + hicn_data_push_trace_t *t = + vlib_add_trace (vm, node, b1, sizeof (*t)); + t->pkt_type = HICN_PKT_TYPE_CONTENT; + t->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX];; + t->next_index = next0; + } + } + + /* Dual loop, X1 */ + while (n_left_from > 0 && n_left_to_next > 0) + { + u8 isv6; + u8 *nameptr; + u16 namelen; + hicn_name_t name; + hicn_header_t *hicn0; + vlib_buffer_t *b0; + u32 bi0; + u32 next0 = next_index; + int ret0; + + /* Prefetch for next iteration. */ + if (n_left_from > 1) + { + vlib_buffer_t *b1; + //hicn_buffer_t * hicnb1; + b1 = vlib_get_buffer (vm, from[1]); + CLIB_PREFETCH (b1, CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH (b1->data, CLIB_CACHE_LINE_BYTES, STORE); + } + /* Dequeue a packet buffer */ + bi0 = from[0]; + from += 1; + n_left_from -= 1; + + b0 = vlib_get_buffer (vm, bi0); + next0 = HICN_DATA_PUSH_NEXT_ERROR_DROP; + + ret0 = hicn_data_parse_pkt (b0, &name, &namelen, &hicn0, &isv6); + nameptr = (u8 *) (&name); + + if (PREDICT_TRUE (ret0 == HICN_ERROR_NONE)) + hicn_new_data (vm, rt, b0, &next0, tnow, nameptr, namelen, isv6); + stats.pkts_data_count++; + + /* Maybe trace */ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + hicn_data_push_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->pkt_type = HICN_PKT_TYPE_CONTENT; + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];; + t->next_index = next0; + } + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, hicn_data_push_node.index, + HICNFWD_ERROR_CACHED, stats.pkts_data_count); + + return (frame->n_vectors); +} + +/* packet trace format function */ +always_inline u8 * +hicn_data_push_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_data_push_trace_t *t = va_arg (*args, hicn_data_push_trace_t *); + + s = format (s, "DATA-STORE: pkt: %d, sw_if_index %d, next index %d\n", + (int) t->pkt_type, t->sw_if_index, t->next_index); + + return (s); +} + + +/* + * Node registration for the data forwarder node + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE(hicn_data_push_node) = +{ + .function = hicn_data_push_fn, + .name = "hicn-data-push", + .vector_size = sizeof(u32), + .runtime_data_bytes = sizeof(hicn_data_push_runtime_t), + .format_trace = hicn_data_push_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(hicn_data_push_error_strings), + .error_strings = hicn_data_push_error_strings, + .n_next_nodes = HICN_DATA_PUSH_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = { + [HICN_DATA_PUSH_NEXT_ERROR_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/error.c b/hicn-plugin/src/error.c new file mode 100755 index 000000000..588ae2398 --- /dev/null +++ b/hicn-plugin/src/error.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "error.h" + +const char *HICN_ERROR_STRING[] = { +#define _(a,b,c) c, + foreach_hicn_error +#undef _ +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/error.h b/hicn-plugin/src/error.h new file mode 100755 index 000000000..978c7f2ca --- /dev/null +++ b/hicn-plugin/src/error.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_ERROR_H__ +#define __HICN_ERROR_H__ + +#define foreach_hicn_error \ + _(NONE, 0, "Ok") \ + _(UNSPECIFIED, -128, "Unspecified Error") \ + _(FACE_NOT_FOUND, -129, "Face not found in Face table") \ + _(FACE_NULL, -130, "Face null") \ + _(FACE_IP_ADJ_NOT_FOUND, -131, "Ip adjacecny for face not found") \ + _(FACE_HW_INT_NOT_FOUND, -132, "Hardware interface not found") \ + _(FACE_NOMEM, -133, "Face table is full") \ + _(FACE_NO_GLOBAL_IP, -134, "No global ip address for face") \ + _(FACE_NOT_FOUND_IN_ENTRY, -135, "Face not found in entry") \ + _(FACE_ALREADY_DELETED, -136, "Face alredy deleted") \ + _(FACE_ALREADY_CREATED, -137, "Face alredy created") \ + _(FWD_NOT_ENABLED, -138, "hICN forwarder not enabled") \ + _(FWD_ALREADY_ENABLED, -139, "hICN forwarder alredy enabled") \ + _(PARSER_UNSUPPORTED_PROTO, -140, "Unsupported protocol") \ + _(PARSER_PKT_INVAL, -141, "Packet null") \ + _(PIT_CONFIG_MINLT_OOB, -142, "Min lifetime ouf of bounds") \ + _(PIT_CONFIG_MAXLT_OOB, -143, "Max lifetime ouf of bounds") \ + _(PIT_CONFIG_MINMAXLT, -144, "Min lifetime grater than max lifetime") \ + _(PIT_CONFIG_DFTLT_OOB, -145, "Default lifetime ouf of bounds") \ + _(PIT_CONFIG_SIZE_OOB, -146, "Pit size ouf of bounds") \ + _(CS_CONFIG_SIZE_OOB, -147, "CS size ouf of bounds") \ + _(CS_CONFIG_RESERVED_OOB, -148, "Reseved CS must be between 0 and 100 (excluded)") \ + _(DPO_CTX_NHOPS_NS, -149, "No space for additional next hop") \ + _(DPO_CTX_NHOPS_EXISTS, -150, "Next hop already in the route") \ + _(DPO_CTX_NOT_FOUND, -151, "Dpo context not found") \ + _(DPO_MGR_ID_NOT_VALID, -152, "Dpo id for strategy and context not valid") \ + _(HASHTB_HASH_NOT_FOUND, -153, "Hash not found in hash table") \ + _(HASHTB_HASH_INVAL, -154, "Error while calculating the hash") \ + _(HASHTB_NOMEM, -155, "Unable to allocate new buckets or nodes") \ + _(HASHTB_INVAL, -156, "Invalid argument") \ + _(HASHTB_KEY_INVAL, -157, "Invalid hashtb key") \ + _(HASHTB_EXIST, -158, "Hash already in hashtable") \ + _(ROUTE_INVAL, -159, "Invalid face id and weight") \ + _(ROUTE_NO_LD, -160, "Expected load balance dpo") \ + _(ROUTE_MLT_LD, -161, "Unexpected mulitple buckets in load balance dpo") \ + _(ROUTE_NO_INSERT, -162, "Unable to insert a new FIB entry") \ + _(ROUTE_DPO_NO_HICN, -163, "Dpo is not of type hICN") \ + _(ROUTE_NOT_FOUND, -164, "Route not found in FIB") \ + _(ROUTE_NOT_UPDATED, -165, "Unable to update route") \ + _(ROUTE_ALREADY_EXISTS, -166, "Route already in FIB") \ + _(CLI_INVAL, -167, "Invalid input") \ + _(PUNT_INVAL, -168, "Invalid prefix or subnet or interface") \ + _(PUNT_TBL_NOT_FOUND, -169, "Vnet table not found") \ + _(PUNT_TBL_EXIST, -170, "Vnet table already created") \ + _(PUNT_SSN_NOT_FOUND, -171, "Vnet session not found") \ + _(PUNT_SSN_EXIST, -172, "Vnet session already created") \ + _(PUNT_SKIP_NOT_SUPPORTED, -173, "Skip size not supported. Skip must be <= 1") \ + _(PUNT_NOMEM, -174, "Unable to allocate skip_mask") \ + _(IPS_ADDR_TYPE_NONUNIFORM, -175, "Src and dst addr have different ip types") \ + _(FACE_TYPE_EXISTS, -176, "Face type already registered") \ + _(NO_BUFFERS, -177, "No vlib_buffer available for packet cloning.") \ + _(NOT_IMPLEMENTED, -178, "Function not yet implemented") \ + _(IFACE_IP_ADJ_NOT_FOUND, -179, "IP adjacency on incomplete face not available") \ + _(APPFACE_ALREADY_ENABLED, -180, "Application face already enabled on interface") \ + _(APPFACE_FEATURE, -181, "Error while enabling app face feature") \ + _(APPFACE_NOT_FOUND, -182, "Application face not found") \ + _(APPFACE_PROD_PREFIX_NULL, -183, "Prefix must not be null for producer face") \ + _(MW_STRATEGY_NH_NOT_FOUND, -184, "Next hop not found") \ + _(MW_STRATEGY_SET, -185, "Error while setting weight for next hop") \ + _(STRATEGY_NOT_FOUND, -186, "Strategy not found") + + +typedef enum +{ +#define _(a,b,c) HICN_ERROR_##a = (b), + foreach_hicn_error +#undef _ + HICN_N_ERROR, +} hicn_error_t; + +extern const char *HICN_ERROR_STRING[]; + +#define get_error_string(errno) (char *)(errno ? HICN_ERROR_STRING[(-errno) - 127] : HICN_ERROR_STRING[errno]) + +#endif /* //__HICN_ERROR_H__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/face_db.h b/hicn-plugin/src/face_db.h new file mode 100755 index 000000000..7b8a08879 --- /dev/null +++ b/hicn-plugin/src/face_db.h @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_FACE_DB_H__ +#define __HICN_FACE_DB_H__ + +#include +#include "faces/face.h" + +/** + * @File + * + * Define a face db that is store in every pit entry. A face db containes a list + * of incoming faces for interest packets that are used to forward data packets + * on the interests' reverse path + */ + +/* Must be power of two */ +#define HICN_FACE_DB_INLINE_FACES 4 + +#define HICN_PIT_N_HOP_BITMAP_SIZE HICN_PARAM_PIT_ENTRY_PHOPS_MAX + +#define HICN_PIT_N_HOP_BUCKET (HICN_PARAM_PIT_ENTRY_PHOPS_MAX - HICN_FACE_DB_INLINE_FACES) + +STATIC_ASSERT ((HICN_PIT_N_HOP_BUCKET & (HICN_PIT_N_HOP_BUCKET - 1)) == 0, + "HICN_PARAM_PIT_ENTRY_PHOP_MAX must be a power of 2 + 4"); + +/* Takes 2 cache lines */ +typedef struct __attribute__ ((packed)) hicn_face_bucket_s +{ + /* Array of indexes of virtual faces */ + dpo_id_t faces[HICN_PIT_N_HOP_BUCKET]; + + CLIB_CACHE_LINE_ALIGN_MARK (cache_line1); + + /* Used to check if interests are retransmission */ + u8 bitmap[HICN_PIT_N_HOP_BITMAP_SIZE]; + +} hicn_face_bucket_t; + +extern hicn_face_bucket_t *hicn_face_bucket_pool; + +typedef struct __attribute__ ((packed)) hicn_face_db_s +{ + /* 19B + 1B = 20B */ + /* Equal to one or zero */ + u8 is_overflow; + + /* Number of faces in the last bucket */ + /* Or next availabe entry for storing a dpo_id_t */ + /* 20B + 4B = 24B */ + u32 n_faces; + + /* 24B + 32B (8*4) = 56B */ + /* Array of indexes of virtual faces */ + dpo_id_t inline_faces[HICN_FACE_DB_INLINE_FACES]; + + /* 56B + 4B = 60B */ + u32 next_bucket; + + /* 60B + 4B = 64B */ + u32 align; + //align back to 64 + +} hicn_face_db_t; + +always_inline dpo_id_t * +hicn_face_db_get_dpo_face (u32 index, hicn_face_db_t * face_db) +{ + ASSERT (index < face_db->n_faces); + + return index < HICN_FACE_DB_INLINE_FACES ? &(face_db->inline_faces[index]) : + &(pool_elt_at_index (hicn_face_bucket_pool, face_db->next_bucket)->faces + [(index - HICN_FACE_DB_INLINE_FACES) & (HICN_PIT_N_HOP_BUCKET - 1)]); +} + +always_inline void +hicn_face_db_init (int max_element) +{ + pool_init_fixed (hicn_face_bucket_pool, max_element); +} + +always_inline hicn_face_bucket_t * +hicn_face_db_get_bucket (u32 bucket_index) +{ + return pool_elt_at_index (hicn_face_bucket_pool, bucket_index); +} + +always_inline void +hicn_face_db_add_face_dpo (dpo_id_t * dpo, hicn_face_db_t * face_db) +{ + ASSERT (dpo->dpoi_index != ~0); + + hicn_face_bucket_t *faces_bkt = + pool_elt_at_index (hicn_face_bucket_pool, face_db->next_bucket); + + dpo_id_t *face = + face_db->n_faces < + HICN_FACE_DB_INLINE_FACES ? &(face_db->inline_faces[face_db->n_faces]) : + &(faces_bkt->faces + [(face_db->n_faces - + HICN_FACE_DB_INLINE_FACES) & (HICN_PIT_N_HOP_BUCKET - 1)]); + + clib_memcpy (face, dpo, sizeof (dpo_id_t)); + + /* This access the dpoi to increase the lock */ + dpo_lock (dpo); + + u32 bitmap_index = dpo->dpoi_index % HICN_PIT_N_HOP_BITMAP_SIZE; + faces_bkt->bitmap[bitmap_index] |= 0x01; + face_db->n_faces++; +} + +always_inline u8 +hicn_face_search (dpo_id_t * dpo, hicn_face_db_t * face_db) +{ + hicn_face_bucket_t *faces_bkt = + pool_elt_at_index (hicn_face_bucket_pool, face_db->next_bucket); + u32 bitmap_index = dpo->dpoi_index % HICN_PIT_N_HOP_BITMAP_SIZE; + + return faces_bkt->bitmap[bitmap_index] & 0x01; +} + +always_inline void +hicn_faces_flush (hicn_face_db_t * face_db) +{ + hicn_face_bucket_t *faces_bkt = + pool_elt_at_index (hicn_face_bucket_pool, face_db->next_bucket); + clib_memset_u64 (&(faces_bkt->bitmap), 0, HICN_PIT_N_HOP_BITMAP_SIZE / 8); + face_db->n_faces = 0; + pool_put_index (hicn_face_bucket_pool, face_db->next_bucket); +} + + +#endif /* // __HICN_FACE_DB_H__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/faces/app/address_mgr.c b/hicn-plugin/src/faces/app/address_mgr.c new file mode 100755 index 000000000..76a7e0f6d --- /dev/null +++ b/hicn-plugin/src/faces/app/address_mgr.c @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Copyright (c) 2017-2019 by cisco systems inc. All rights reserved. + * + */ + +#include + +#include +#include +#include //ip4_add_del_ip_address +#include //ip6_add_del_ip_address +#include //FIB_PROTOCOL_IP4/6, FIB_NODE_INDEX_INVALID +#include //FIB_SOURCE_PLUGIN_HI +#include +#include +#include //appif_flags +#include //vnet_sw_interface_set_flags + +#include "address_mgr.h" +#include "../../hicn.h" +#include "../../infra.h" +#include "../../error.h" +#include "../face.h" +#include "../ip/face_ip.h" +#include "../../strategy_dpo_ctx.h" +#include "../../route.h" + +typedef struct address_mgr_main_s +{ + ip4_address_t next_ip4_local_addr; + ip6_address_t next_ip6_local_addr; +} address_mgr_main_t; + +address_mgr_main_t address_mgr_main; + +static void +increment_v4_address (ip4_address_t * a, u32 val) +{ + u32 v; + + v = clib_net_to_host_u32 (a->as_u32) + val; + a->as_u32 = clib_host_to_net_u32 (v); +} + +static void +increment_v6_address (ip6_address_t * a, u64 val) +{ + u64 v; + + v = clib_net_to_host_u64 (a->as_u64[1]) + val; + a->as_u64[1] = clib_host_to_net_u64 (v); +} + +void +get_two_ip4_addresses (ip4_address_t * appif_addr, ip4_address_t * nh_addr) +{ + /* We want two consecutives address that fall into a /31 mask */ + if (address_mgr_main.next_ip4_local_addr.as_u8[3] & 0x01) + increment_v4_address (&(address_mgr_main.next_ip4_local_addr), 1); + + *appif_addr = address_mgr_main.next_ip4_local_addr; + increment_v4_address (&(address_mgr_main.next_ip4_local_addr), 1); + *nh_addr = address_mgr_main.next_ip4_local_addr; + fib_prefix_t fib_pfx; + fib_node_index_t fib_entry_index = FIB_NODE_INDEX_INVALID; + u32 fib_index; + + fib_pfx.fp_proto = FIB_PROTOCOL_IP4; + fib_pfx.fp_len = ADDR_MGR_IP4_LEN; + /* At this point the face exists in the face table */ + do + { + /* Check if the route already exist in the fib */ + fib_pfx.fp_addr = to_ip46 ( /* is_v6 */ 0, appif_addr->as_u8); + fib_index = fib_table_find_or_create_and_lock (fib_pfx.fp_proto, + HICN_FIB_TABLE, + FIB_SOURCE_PLUGIN_HI); + fib_entry_index = fib_table_lookup_exact_match (fib_index, &fib_pfx); + fib_table_unlock (fib_index, fib_pfx.fp_proto, FIB_SOURCE_PLUGIN_HI); + if (fib_entry_index != FIB_NODE_INDEX_INVALID) + { + fib_pfx.fp_addr = to_ip46 ( /* is_v6 */ 0, nh_addr->as_u8); + fib_index = fib_table_find_or_create_and_lock (fib_pfx.fp_proto, + HICN_FIB_TABLE, + FIB_SOURCE_PLUGIN_HI); + fib_entry_index = + fib_table_lookup_exact_match (fib_index, &fib_pfx); + fib_table_unlock (fib_index, fib_pfx.fp_proto, + FIB_SOURCE_PLUGIN_HI); + } + if (fib_entry_index != FIB_NODE_INDEX_INVALID) + { + increment_v4_address (appif_addr, 2); + increment_v4_address (nh_addr, 2); + } + } + while (fib_entry_index != FIB_NODE_INDEX_INVALID); + + address_mgr_main.next_ip4_local_addr = *nh_addr; + increment_v4_address (&(address_mgr_main.next_ip4_local_addr), 1); +} + +void +get_two_ip6_addresses (ip6_address_t * appif_addr, ip6_address_t * nh_addr) +{ + + /* We want two consecutives address that fall into a /127 mask */ + if (address_mgr_main.next_ip6_local_addr.as_u8[15] & 0x01) + increment_v6_address (&(address_mgr_main.next_ip6_local_addr), 1); + + *appif_addr = address_mgr_main.next_ip6_local_addr; + increment_v6_address (&(address_mgr_main.next_ip6_local_addr), 1); + *nh_addr = address_mgr_main.next_ip6_local_addr; + + + fib_prefix_t fib_pfx; + fib_node_index_t fib_entry_index = FIB_NODE_INDEX_INVALID; + u32 fib_index; + + fib_pfx.fp_proto = FIB_PROTOCOL_IP6; + fib_pfx.fp_len = ADDR_MGR_IP6_LEN; + /* At this point the face exists in the face table */ + do + { + /* Check if the route already exist in the fib */ + fib_pfx.fp_addr = to_ip46 ( /* is_v6 */ 1, appif_addr->as_u8); + fib_index = fib_table_find_or_create_and_lock (fib_pfx.fp_proto, + HICN_FIB_TABLE, + FIB_SOURCE_PLUGIN_HI); + fib_entry_index = fib_table_lookup_exact_match (fib_index, &fib_pfx); + fib_table_unlock (fib_index, fib_pfx.fp_proto, FIB_SOURCE_PLUGIN_HI); + if (fib_entry_index != FIB_NODE_INDEX_INVALID) + { + fib_pfx.fp_addr = to_ip46 ( /* is_v6 */ 0, nh_addr->as_u8); + fib_index = fib_table_find_or_create_and_lock (fib_pfx.fp_proto, + HICN_FIB_TABLE, + FIB_SOURCE_PLUGIN_HI); + fib_entry_index = + fib_table_lookup_exact_match (fib_index, &fib_pfx); + fib_table_unlock (fib_index, fib_pfx.fp_proto, + FIB_SOURCE_PLUGIN_HI); + } + if (fib_entry_index != FIB_NODE_INDEX_INVALID) + { + increment_v6_address (appif_addr, 2); + increment_v6_address (nh_addr, 2); + } + } + while (fib_entry_index != FIB_NODE_INDEX_INVALID); + + address_mgr_main.next_ip6_local_addr = *nh_addr; + increment_v6_address (&(address_mgr_main.next_ip6_local_addr), 1); +} + +ip4_address_t +get_ip4_address () +{ + ip4_address_t *prefix = &address_mgr_main.next_ip4_local_addr; + fib_prefix_t fib_pfx; + fib_node_index_t fib_entry_index = FIB_NODE_INDEX_INVALID; + u32 fib_index; + + fib_pfx.fp_proto = FIB_PROTOCOL_IP4; + fib_pfx.fp_len = ADDR_MGR_IP4_LEN; + /* At this point the face exists in the face table */ + do + { + /* Check if the route already exist in the fib */ + fib_pfx.fp_addr = to_ip46 ( /* is_v6 */ 0, prefix->as_u8); + fib_index = fib_table_find_or_create_and_lock (fib_pfx.fp_proto, + HICN_FIB_TABLE, + FIB_SOURCE_PLUGIN_HI); + fib_entry_index = fib_table_lookup_exact_match (fib_index, &fib_pfx); + fib_table_unlock (fib_index, fib_pfx.fp_proto, FIB_SOURCE_PLUGIN_HI); + increment_v4_address (prefix, 1); + } + while (fib_entry_index != FIB_NODE_INDEX_INVALID); + + return fib_pfx.fp_addr.ip4; +} + +ip6_address_t +get_ip6_address () +{ + ip6_address_t *prefix = &address_mgr_main.next_ip6_local_addr; + fib_prefix_t fib_pfx; + fib_node_index_t fib_entry_index = FIB_NODE_INDEX_INVALID; + u32 fib_index; + + fib_pfx.fp_proto = FIB_PROTOCOL_IP6; + fib_pfx.fp_len = ADDR_MGR_IP6_LEN; + /* At this point the face exists in the face table */ + do + { + /* Check if the route already exist in the fib */ + fib_pfx.fp_addr = to_ip46 ( /* is_v6 */ 1, prefix->as_u8); + fib_index = fib_table_find_or_create_and_lock (fib_pfx.fp_proto, + HICN_FIB_TABLE, + FIB_SOURCE_PLUGIN_HI); + fib_entry_index = fib_table_lookup_exact_match (fib_index, &fib_pfx); + fib_table_unlock (fib_index, fib_pfx.fp_proto, FIB_SOURCE_PLUGIN_HI); + increment_v6_address (prefix, 1); + } + while (fib_entry_index != FIB_NODE_INDEX_INVALID); + + return fib_pfx.fp_addr.ip6; +} + +void +address_mgr_init () +{ + + address_mgr_main.next_ip4_local_addr.as_u8[0] = 169; + address_mgr_main.next_ip4_local_addr.as_u8[1] = 254; + address_mgr_main.next_ip4_local_addr.as_u8[2] = 1; + address_mgr_main.next_ip4_local_addr.as_u8[3] = 1; + + ip6_address_set_zero (&address_mgr_main.next_ip6_local_addr); + address_mgr_main.next_ip6_local_addr.as_u16[0] = + clib_host_to_net_u16 (0xfc00); + address_mgr_main.next_ip6_local_addr.as_u8[15] = 1; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/faces/app/address_mgr.h b/hicn-plugin/src/faces/app/address_mgr.h new file mode 100755 index 000000000..99450dcdd --- /dev/null +++ b/hicn-plugin/src/faces/app/address_mgr.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ADDRESS_MGR_H_ +#define _ADDRESS_MGR_H_ + +/** + * @file + * + * @brief Address manager. + * + * Address manager that maintains a pool of ip4 and ip6 addresses to assign to + * an interface. + */ + +#define ADDR_MGR_IP4_LEN 32 +#define ADDR_MGR_IP4_CONS_LEN 31 +#define ADDR_MGR_IP6_LEN 128 +#define ADDR_MGR_IP6_CONS_LEN 127 + +/** + * @brief Get two consecutive IP v4 addresses from the same /31 subnet + * + * @param addr1 first ip address with the least significant bit set to 0 + * @param addr2 second ip address with the least significant bit set to 1 + */ +void get_two_ip4_addresses (ip4_address_t * addr1, ip4_address_t * addr2); + +/** + * @brief Get two consecutive IP v6 addresses from the same /126 subnet + * + * @param addr1 first ip address with the least significant bit set to 0 + * @param addr2 second ip address with the least significant bit set to 1 + */ +void get_two_ip6_addresses (ip6_address_t * addr1, ip6_address_t * addr2); + +/** + * @brief Get one IP v4 address + * + * @return ip address + */ +ip4_address_t get_ip4_address (void); + +/** + * @brief Get one IP v6 address + * + * @return ip address + */ +ip6_address_t get_ip6_address (void); + +/** + * @brief Init the address manager + */ +void address_mgr_init (void); + +#endif /* _ADDRESS_MGR_ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/faces/app/face_app_cli.c b/hicn-plugin/src/faces/app/face_app_cli.c new file mode 100755 index 000000000..d55e990de --- /dev/null +++ b/hicn-plugin/src/faces/app/face_app_cli.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include "../ip/face_ip.h" +#include "../ip/dpo_ip.h" +#include "../face.h" +#include "face_prod.h" +#include "face_cons.h" + +#define HICN_FACE_NONE 0 +#define HICN_FACE_DELETE 1 +#define HICN_FACE_ADD 2 + +static clib_error_t * +hicn_face_app_cli_set_command_fn (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + vnet_main_t *vnm = vnet_get_main (); + ip46_address_t prefix; + hicn_face_id_t face_id = HICN_FACE_NULL; + u32 cs_reserved = HICN_PARAM_FACE_DFT_CS_RESERVED; + int ret = HICN_ERROR_NONE; + int sw_if; + int face_op = HICN_FACE_NONE; + int prod = 0; + int len; + + /* Get a line of input. */ + unformat_input_t _line_input, *line_input = &_line_input; + if (!unformat_user (main_input, unformat_line_input, line_input)) + { + return (0); + } + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "del")) + { + face_op = HICN_FACE_DELETE; + } + else if (face_op == HICN_FACE_DELETE + && unformat (line_input, "id %d", &face_id)) + ; + else if (unformat (line_input, "add")) + { + face_op = HICN_FACE_ADD; + } + else if (face_op == HICN_FACE_ADD) + { + if (unformat (line_input, "intfc %U", + unformat_vnet_sw_interface, vnm, &sw_if)) + ; + else + if (unformat + (line_input, "prod prefix %U/%d", unformat_ip46_address, + &prefix, IP46_TYPE_ANY, &len)) + { + prod = 1; + } + else if (prod && unformat (line_input, "cs_size %d", &cs_reserved)) + ; + else if (unformat (line_input, "cons")) + ; + else + { + return clib_error_return (0, "%s '%U'", + get_error_string + (HICN_ERROR_CLI_INVAL), + format_unformat_error, line_input); + } + } + else + { + return clib_error_return (0, "%s '%U'", + get_error_string (HICN_ERROR_CLI_INVAL), + format_unformat_error, line_input); + } + } + + if (face_id != HICN_FACE_NULL) + { + + if (!hicn_dpoi_idx_is_valid (face_id)) + { + return clib_error_return (0, "%s, face_id %d not valid", + get_error_string (ret), face_id); + } + } + + int rv; + switch (face_op) + { + case HICN_FACE_ADD: + { + ip46_address_t prod_addr; + ip4_address_t cons_addr4; + ip6_address_t cons_addr6; + + hicn_prefix_t name_prefix = { + .name = prefix, + .len = len, + }; + if (prod) + { + rv = + hicn_face_prod_add (&name_prefix, sw_if, &cs_reserved, + &prod_addr, &face_id); + if (rv == HICN_ERROR_NONE) + { + u8 *sbuf = NULL; + sbuf = + format (sbuf, "Face id: %d, producer address %U", face_id, + format_ip46_address, &prod_addr, + 0 /*IP46_ANY_TYPE */ ); + vlib_cli_output (vm, "%s", sbuf); + } + else + { + return clib_error_return (0, get_error_string (rv)); + } + } + else + { + rv = + hicn_face_cons_add (&cons_addr4, &cons_addr6, sw_if, &face_id); + if (rv == HICN_ERROR_NONE) + { + u8 *sbuf = NULL; + sbuf = + format (sbuf, "Face id: %d, consumer addresses v4 %U v6 %U", + face_id, format_ip4_address, &cons_addr4, + format_ip6_address, &cons_addr6); + vlib_cli_output (vm, "%s", sbuf); + } + else + { + return clib_error_return (0, get_error_string (rv)); + } + } + break; + } + case HICN_FACE_DELETE: + { + hicn_face_t *face = hicn_dpoi_get_from_idx (face_id); + + if (face->shared.flags & HICN_FACE_FLAGS_APPFACE_CONS) + rv = hicn_face_cons_del (face_id); + else + rv = hicn_face_prod_del (face_id); + if (rv == HICN_ERROR_NONE) + { + vlib_cli_output (vm, "Face %d deleted", face_id); + } + else + { + return clib_error_return (0, get_error_string (rv)); + } + break; + } + default: + return clib_error_return (0, "Operation (%d) not implemented", face_op); + break; + } + return (rv == HICN_ERROR_NONE) ? 0 : clib_error_return (0, "%s\n", + get_error_string + (rv)); +} + +/* cli declaration for 'cfg face' */ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (hicn_face_app_cli_set_command, static) = +{ + .path = "hicn face app", + .short_help = "hicn face app {add intfc { prod prefix cs_size } {cons} | {del }", + .function = hicn_face_app_cli_set_command_fn, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/faces/app/face_cons.c b/hicn-plugin/src/faces/app/face_cons.c new file mode 100755 index 000000000..8278b6ab3 --- /dev/null +++ b/hicn-plugin/src/faces/app/face_cons.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "face_cons.h" +#include "address_mgr.h" +#include "../../infra.h" + +int +hicn_face_cons_add (ip4_address_t * nh_addr4, ip6_address_t * nh_addr6, + u32 swif, hicn_face_id_t * faceid) +{ + /* Create the corresponding appif if */ + /* Retrieve a valid local ip address to assign to the appif */ + /* Set the ip address and create the face in the face db */ + + vlib_main_t *vm = vlib_get_main (); + vnet_main_t *vnm = vnet_get_main (); + + hicn_main_t *hm = &hicn_main; + + ip46_address_t if_ip; + ip46_address_reset (&if_ip); + nh_addr4->as_u32 = 0; + nh_addr6->as_u64[0] = 0; + nh_addr6->as_u64[1] = 0; + u32 if_flags = 0; + + if (!hm->is_enabled) + { + return HICN_ERROR_FWD_NOT_ENABLED; + } + if_flags |= VNET_SW_INTERFACE_FLAG_ADMIN_UP; + vnet_sw_interface_set_flags (vnm, swif, if_flags); + + get_two_ip4_addresses (&(if_ip.ip4), nh_addr4); + ip4_add_del_interface_address (vm, + swif, + &(if_ip.ip4), + ADDR_MGR_IP4_CONS_LEN, 0 /* is_del */ ); + + ip46_address_t nh_addr = to_ip46 (0, (u8 *) nh_addr4); + + hicn_iface_ip_add (&if_ip, &nh_addr, swif, faceid); + + hicn_face_t *face = hicn_dpoi_get_from_idx (*faceid); + face->shared.flags |= HICN_FACE_FLAGS_APPFACE_CONS; + + get_two_ip6_addresses (&(if_ip.ip6), nh_addr6); + ip6_add_del_interface_address (vm, + swif, + &(if_ip.ip6), + ADDR_MGR_IP6_CONS_LEN, 0 /* is_del */ ); + + hicn_iface_ip_add (&if_ip, (ip46_address_t *) nh_addr6, swif, faceid); + + face = hicn_dpoi_get_from_idx (*faceid); + face->shared.flags |= HICN_FACE_FLAGS_APPFACE_CONS; + + return vnet_feature_enable_disable ("ip6-unicast", + "hicn-iface-ip6-input", swif, 1, 0, + 0) == + 0 ? HICN_ERROR_NONE : HICN_ERROR_APPFACE_FEATURE; +} + +int +hicn_face_cons_del (hicn_face_id_t face_id) +{ + hicn_face_t *face = hicn_dpoi_get_from_idx (face_id); + + if (face->shared.flags & HICN_FACE_FLAGS_APPFACE_CONS) + { + int ret = hicn_face_ip_del (face_id); + + return ret == + HICN_ERROR_NONE + ? (vnet_feature_enable_disable + ("ip6-unicast", "hicn-iface-ip6-input", face->shared.sw_if, 0, + 0, 0) == 0 ? HICN_ERROR_NONE : HICN_ERROR_APPFACE_FEATURE) : ret; + } + else + { + return HICN_ERROR_APPFACE_NOT_FOUND; + } +} + +u8 * +format_hicn_face_cons (u8 * s, va_list * args) +{ + CLIB_UNUSED (index_t index) = va_arg (*args, index_t); + CLIB_UNUSED (u32 indent) = va_arg (*args, u32); + + s = format (s, " (consumer face)"); + + return s; +} + +/* *INDENT-OFF* */ +VNET_FEATURE_INIT(hicn_cons_app, static)= +{ + .arc_name = "ip6-unicast", + .node_name = "hicn-iface-ip6-input", + .runs_before = VNET_FEATURES("ip6-inacl"), +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/faces/app/face_cons.h b/hicn-plugin/src/faces/app/face_cons.h new file mode 100755 index 000000000..067b45a1f --- /dev/null +++ b/hicn-plugin/src/faces/app/face_cons.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _FACE_CONSUMER_H_ +#define _FACE_CONSUMER_H_ + +#include +#include "../face.h" + +/** + * @file + * + * @brief Consumer application face. + * + * A consumer application face is built upon an ip face and identify a local + * consumer application (co-located with the forwarder) that acts as a + * consumer. The interface used by the consumer application face is + * assumed to be reserved only for hICN traffic (e.g., dedicated memif that + * connects the applictation to the forwarder). Only one application face can be + * assigned to an interface. + * + * In the vlib graph a consumer application face directly connect the + * device-input node to the hicn-vface-ip node. + */ + +/** + * @brief Add a new consumer application face + * + * The method creates the internal ip face and set the ip address to the interface. + * @param nh_addr4 ipv4 address to assign to interface used by the application to + * send interest to the consumer face + * @param nh_addr6 ipv6 address to assign to interface used by the application to + * send interest to the consumer face + * @param swif interface associated to the face + */ +int +hicn_face_cons_add (ip4_address_t * nh_addr4, ip6_address_t * nh_addr6, + u32 swif, hicn_face_id_t * faceid); + +/** + * @brief Delete an existing consumer application face + * + * @param face_id Id of the consumer application face + */ +int hicn_face_cons_del (hicn_face_id_t face_id); + +/** + * @brief Format an application consumer face + * + * @param s Pointer to a previous string. If null it will be initialize + * @param args Array storing input values. Expected u32 face_id and u32 indent + * @return String with the formatted face + */ +u8 *format_hicn_face_cons (u8 * s, va_list * args); + + +#endif /* _FACE_CONSUMER_H_ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/faces/app/face_prod.c b/hicn-plugin/src/faces/app/face_prod.c new file mode 100755 index 000000000..d06fe2ff3 --- /dev/null +++ b/hicn-plugin/src/faces/app/face_prod.c @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "face_prod.h" +#include "address_mgr.h" +#include "../../infra.h" +#include "../../route.h" +#include "../../cache_policies/cs_lru.h" + +hicn_face_prod_state_t *face_state_vec; + +/* used to check if an interface is already in the vector */ +u32 *face_state_pool; + +static int +hicn_app_state_create (u32 swif, hicn_prefix_t * prefix) +{ + /* Make sure that the pool is not empty */ + pool_validate_index (face_state_pool, 0); + + u32 *swif_app; + u8 found = 0; + /* *INDENT-OFF* */ + pool_foreach (swif_app, face_state_pool,{ + if (*swif_app == swif) + { + found = 1; + } + } + ); + /* *INDENT-ON* */ + + + if (found) + return HICN_ERROR_APPFACE_ALREADY_ENABLED; + + + /* Create the appif and store in the vector */ + vec_validate (face_state_vec, swif); + clib_memcpy (&(face_state_vec[swif].prefix), prefix, + sizeof (hicn_prefix_t)); + + /* Set as busy the element in the vector */ + pool_get (face_state_pool, swif_app); + *swif_app = swif; + + int ret = HICN_ERROR_NONE; + if (ip46_address_is_ip4 (&(prefix->name))) + { + ret = + vnet_feature_enable_disable ("ip4-unicast", "hicn-face-prod-input", + swif, 1, 0, 0); + } + else + { + ret = + vnet_feature_enable_disable ("ip6-unicast", "hicn-face-prod-input", + swif, 1, 0, 0); + } + + return ret == 0 ? HICN_ERROR_NONE : HICN_ERROR_APPFACE_FEATURE; +} + +static int +hicn_app_state_del (u32 swif) +{ + /* Make sure that the pool is not empty */ + pool_validate_index (face_state_pool, 0); + + u32 *temp; + u32 *swif_app = NULL; + u8 found = 0; + ip46_address_t *prefix_addr; + /* *INDENT-OFF* */ + pool_foreach (temp, face_state_pool,{ + if (*temp == swif) + { + found = 1; + swif_app = temp; + } + } + ); + /* *INDENT-ON* */ + + prefix_addr = &(face_state_vec[swif].prefix.name); + if (!found) + return HICN_ERROR_APPFACE_NOT_FOUND; + + int ret = HICN_ERROR_NONE; + if (ip46_address_is_ip4 (prefix_addr)) + { + ret = + vnet_feature_enable_disable ("ip4-unicast", "hicn-face-prod-input", + swif, 0, 0, 0); + } + else + { + ret = + vnet_feature_enable_disable ("ip6-unicast", "hicn-face-prod-input", + swif, 0, 0, 0); + } + + pool_put (face_state_pool, swif_app); + memset (&face_state_vec[swif], 0, sizeof (hicn_face_prod_state_t)); + + return ret == 0 ? HICN_ERROR_NONE : HICN_ERROR_APPFACE_FEATURE; +} + +int +hicn_face_prod_add (hicn_prefix_t * prefix, u32 sw_if, u32 * cs_reserved, + ip46_address_t * prod_addr, hicn_face_id_t * faceid) +{ + vlib_main_t *vm = vlib_get_main (); + vnet_main_t *vnm = vnet_get_main (); + + hicn_main_t *hm = &hicn_main; + + ip46_address_t app_ip; + u32 if_flags = 0; + + if (!hm->is_enabled) + { + return HICN_ERROR_FWD_NOT_ENABLED; + } + int ret = HICN_ERROR_NONE; + hicn_face_t *face = NULL; + + if_flags |= VNET_SW_INTERFACE_FLAG_ADMIN_UP; + vnet_sw_interface_set_flags (vnm, sw_if, if_flags); + + if (ip46_address_is_zero (&prefix->name)) + { + return HICN_ERROR_APPFACE_PROD_PREFIX_NULL; + } + /* + * Check if a producer face is already existing for the same prefix + * and sw_if + */ + if (ip46_address_is_ip4 (&prefix->name)) + { + face = + hicn_face_ip4_get (&(prefix->name.ip4), sw_if, + &hicn_face_ip_remote_hashtb); + } + else + { + face = + hicn_face_ip6_get (&(prefix->name.ip6), sw_if, + &hicn_face_ip_remote_hashtb); + if (face != NULL) + return HICN_ERROR_FACE_ALREADY_CREATED; + } + + if (face != NULL) + { + if (!(face->shared.flags & HICN_FACE_FLAGS_DELETED)) + return HICN_ERROR_FACE_ALREADY_CREATED; + + /* + * Something went worng, a consumer face exists for the + * producer's prefix. + */ + /* It should never happens, this is a safety check. */ + if (face->shared.flags & HICN_FACE_FLAGS_APPFACE_CONS) + return HICN_ERROR_FACE_ALREADY_CREATED; + + /* If the face exists but is marked as deleted, undelete it */ + if (face->shared.flags & HICN_FACE_FLAGS_DELETED) + { + /* + * remove the deleted flag and retrieve the face + * local addr + */ + face->shared.flags &= HICN_FACE_FLAGS_DELETED; + hicn_face_prod_t *prod_face = (hicn_face_prod_t *) face->data; + app_ip = prod_face->ip_face.local_addr; + } + } + else + { + /* Otherwise create the face */ + if (ip46_address_is_ip4 (&prefix->name)) + { + /* + * Otherwise retrieve an ip address to assign as a + * local ip addr. + */ + ip4_address_t app_ip4 = get_ip4_address (); + ip4_add_del_interface_address (vm, + sw_if, + &app_ip4, + ADDR_MGR_IP4_CONS_LEN, + 0 /* is_del */ ); + app_ip = to_ip46 ( /* isv6 */ 0, app_ip4.as_u8); + } + else + { + ip6_address_t app_ip6 = get_ip6_address (); + ip6_add_del_interface_address (vm, + sw_if, + &app_ip6, + ADDR_MGR_IP6_CONS_LEN, + 0 /* is_del */ ); + app_ip = to_ip46 ( /* isv6 */ 1, app_ip6.as_u8); + } + + /* + * Special case: the nh_addr in the face is the appif ip + * address + */ + ret = hicn_face_ip_add (&app_ip, &(prefix->name), sw_if, faceid); + + face = hicn_dpoi_get_from_idx (*faceid); + + face->shared.flags |= HICN_FACE_FLAGS_APPFACE_PROD; + + hicn_face_prod_t *prod_face = (hicn_face_prod_t *) face->data; + + /* + * For the moment we keep them here although it would be good + * to create a different face for appface + */ + prod_face->policy_vft.hicn_cs_insert = hicn_cs_lru.hicn_cs_insert; + prod_face->policy_vft.hicn_cs_update = hicn_cs_lru.hicn_cs_update; + prod_face->policy_vft.hicn_cs_dequeue = hicn_cs_lru.hicn_cs_dequeue; + prod_face->policy_vft.hicn_cs_delete_get = + hicn_cs_lru.hicn_cs_delete_get; + prod_face->policy_vft.hicn_cs_trim = hicn_cs_lru.hicn_cs_trim; + + } + + if (ret == HICN_ERROR_NONE + && hicn_face_prod_set_lru_max (*faceid, cs_reserved) == HICN_ERROR_NONE) + { + hicn_app_state_create (sw_if, prefix); + ret = hicn_route_add (faceid, 1, &(prefix->name), prefix->len); + } + + *prod_addr = app_ip; + + /* Cleanup in case of something went wrong. */ + if (ret) + { + hicn_app_state_del (sw_if); + + if (*faceid != HICN_FACE_NULL) + hicn_face_ip_del (*faceid); + } + return ret; +} + +int +hicn_face_prod_del (hicn_face_id_t face_id) +{ + hicn_face_t *face = hicn_dpoi_get_from_idx (face_id); + + if (face->shared.flags & HICN_FACE_FLAGS_APPFACE_PROD) + { + hicn_face_prod_t *prod_face = (hicn_face_prod_t *) face->data; + /* Free the CS reserved for the face */ + hicn_main.pitcs.pcs_app_max += prod_face->policy.max; + hicn_main.pitcs.pcs_app_count -= prod_face->policy.max; + prod_face->policy.max = 0; + + /* Remove the face from the fib */ + hicn_route_del_nhop (&(face_state_vec[face->shared.sw_if].prefix.name), + (face_state_vec[face->shared.sw_if].prefix.len), + face_id); + + int ret = hicn_face_ip_del (face_id); + return ret == + HICN_ERROR_NONE ? hicn_app_state_del (face->shared.sw_if) : ret; + } + else + { + return HICN_ERROR_APPFACE_NOT_FOUND; + } +} + +int +hicn_face_prod_set_lru_max (hicn_face_id_t face_id, u32 * requested_size) +{ + int ret = HICN_ERROR_NONE; + vlib_main_t *vm = vlib_get_main (); + hicn_face_t *face; + hicn_face_prod_t *face_prod; + + if (!hicn_infra_fwdr_initialized) + { + ret = HICN_ERROR_FWD_NOT_ENABLED; + vlib_cli_output (vm, "hicn: %s\n", get_error_string (ret)); + return ret; + } + face = hicn_dpoi_get_from_idx (face_id); + face_prod = (hicn_face_prod_t *) face->data; + + if (face == NULL) + return HICN_ERROR_FACE_NOT_FOUND; + + if (*requested_size > HICN_PARAM_FACE_MAX_CS_RESERVED) + *requested_size = HICN_PARAM_FACE_MAX_CS_RESERVED; + + uint32_t available = + hicn_main.pitcs.pcs_app_max - hicn_main.pitcs.pcs_app_count; + + if (*requested_size > available) + *requested_size = available; + + face_prod->policy.max = *requested_size; + face_prod->policy.count = 0; + face_prod->policy.head = face_prod->policy.tail = 0; + + hicn_main.pitcs.pcs_app_count += *requested_size; + + return ret; +} + +u8 * +format_hicn_face_prod (u8 * s, va_list * args) +{ + index_t index = va_arg (*args, index_t); + CLIB_UNUSED (u32 indent) = va_arg (*args, u32); + hicn_face_t *face; + hicn_face_prod_t *prod_face; + + face = hicn_dpoi_get_from_idx (index); + prod_face = (hicn_face_prod_t *) face->data; + + s = + format (s, " (producer face: CS size %d, data cached %d)", + prod_face->policy.max, prod_face->policy.count); + + return s; +} + +/* *INDENT-OFF* */ +VNET_FEATURE_INIT(hicn_prod_app_input_ip6, static)= +{ + .arc_name = "ip6-unicast", + .node_name = "hicn-face-prod-input", + .runs_before = VNET_FEATURES("ip6-inacl"), +}; +/* *INDENT-ON* */ + +/* *INDENT-OFF* */ +VNET_FEATURE_INIT(hicn_prod_app_input_ip4, static)= +{ + .arc_name = "ip4-unicast", + .node_name = "hicn-face-prod-input", + .runs_before = VNET_FEATURES("ip4-inacl"), +}; +/* *INDENT-ON* */ + + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/faces/app/face_prod.h b/hicn-plugin/src/faces/app/face_prod.h new file mode 100755 index 000000000..89b74680b --- /dev/null +++ b/hicn-plugin/src/faces/app/face_prod.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _FACE_PRODUCER_H_ +#define _FACE_PRODUCER_H_ + +#include "../../cache_policies/cs_policy.h" +#include "../ip/face_ip.h" + +/** + * @file + * + * @brief Producer application face. + * + * A producer application face is built upon an ip face and identify a local + * producer application (co-located with the forwarder) that acts as a producer. In the + * current design an application face is either a face towards a consumer face + * or towards a producer. The interface used by the producer application face is + * assumed to be reserved only for hICN traffic (e.g., dedicated memif that + * connects the applictation to the forwarder). Only one application face can be + * assigned to an interface. + * + * To each producer application face it is assigned a portion of the CS. Every + * data arriving to a producer application will be stored in the portion of the + * CS assigned to the face. The eviction policy is defined in the + * face. Available eviction faces are list in the /cache_policy folder. + * + * In the vlib graph a producer application face is directly connected to the + * device-input node (with the node hicn-face-prod-input) and passes every packet to + * the hicn-face-ip node. + */ + +/** + * @brief Producer application face state that refer to the hICN producer socket + * created by the application. + * + */ +typedef struct +{ + hicn_prefix_t prefix; +} hicn_face_prod_state_t; + +extern hicn_face_prod_state_t *face_state_vec; + +typedef struct __attribute__ ((packed)) hicn_face_prod_t_ +{ + hicn_face_ip_t ip_face; + + hicn_cs_policy_t policy; + hicn_cs_policy_vft_t policy_vft; + +} hicn_face_prod_t; + +/** + * @brief Add a new producer application face + * + * The method creates the internal ip face and the state specific to the + * producer application face. This method setups a route in the FIB for the + * producer's prefix. + * @param prefix hicn prefix name assigned to the producer face + * @param len length of the prefix + * @param swif interface associated to the face + * @param cs_reserved return the amount of cs assigned to the face + * @param prod_addr address to assign to interface used by the appliction to + * send data to the producer face + */ +int +hicn_face_prod_add (hicn_prefix_t * prefix, u32 swif, u32 * cs_reserved, + ip46_address_t * prod_addr, hicn_face_id_t * faceid); + +/** + * @brief Delete an existing application face + * + * @param faceid id of the face to remove + */ +int hicn_face_prod_del (hicn_face_id_t faceid); + +/** + * @brief Set lru queue size for an app face + * + * @param face_id Id of the producer application face + */ +int hicn_face_prod_set_lru_max (hicn_face_id_t face_id, u32 * requested_size); + +/** + * @brief Format an application producer face + * + * @param s Pointer to a previous string. If null it will be initialize + * @param args Array storing input values. Expected u32 face_id and u32 indent + * @return String with the formatted face + */ +u8 *format_hicn_face_prod (u8 * s, va_list * args); + + +#endif /* _FACE_PROD_H_ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/faces/app/face_prod_node.c b/hicn-plugin/src/faces/app/face_prod_node.c new file mode 100755 index 000000000..2e746a703 --- /dev/null +++ b/hicn-plugin/src/faces/app/face_prod_node.c @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * + * @brief Application interface node + * + * This node runs after the device-input node and perfoms some safety checks in + * order to avoid unespected interest and data (i.e., hICN packets whose name do + * not contain the prefix associated to the application face) + */ + +#include "face_prod.h" +#include "../../hicn_api.h" +#include "../../mgmt.h" + +#define foreach_face_prod_input_error \ + _(NOT_SOCK_PREFIX, "name not in the socket prefix") + +typedef enum +{ +#define _(f,s) FACE_PROD_INPUT_ERROR_##f, + foreach_face_prod_input_error +#undef _ + FACE_PROD_INPUT_N_ERROR, +} face_prod_input_error_t; + +static __clib_unused char *face_prod_input_error_strings[] = { +#define _(n,s) s, + foreach_face_prod_input_error +#undef _ +}; + +/* Node context data */ +typedef struct hicn_face_prod_runtime_s +{ + int id; +} hicn_face_prod_runtime_t; + +typedef struct +{ + u32 next_index; + u32 sw_if_index; +} hicn_face_prod_input_trace_t; + +typedef enum +{ + HICN_FACE_PROD_NEXT_DATA_IP4, + HICN_FACE_PROD_NEXT_DATA_IP6, + HICN_FACE_PROD_NEXT_ERROR_DROP, + HICN_FACE_PROD_N_NEXT, +} hicn_face_prod_next_t; + +vlib_node_registration_t hicn_face_prod_input_node; + +static __clib_unused u8 * +format_face_prod_input_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_face_prod_input_trace_t *t = + va_arg (*args, hicn_face_prod_input_trace_t *); + CLIB_UNUSED (u32 indent) = format_get_indent (s); + + s = format (s, "prod-face: sw_if_index %d next-index %d", + t->sw_if_index, t->next_index); + return s; +} + +static_always_inline int +match_ip4_name (u32 * name, hicn_prefix_t * prefix) +{ + u32 xor = 0; + + xor = *name & prefix->name.ip4.data_u32; + + return xor == prefix->name.ip4.data_u32; +} + +static_always_inline int +match_ip6_name (u32x4 * name, hicn_prefix_t * prefix) +{ + union + { + u32x4 as_u32x4; + u64 as_u64[2]; + u32 as_u32[4]; + } xor_sum __attribute__ ((aligned (sizeof (u32x4)))); + +#ifdef CLIB_HAVE_VEC128 + if (U32X4_ALIGNED (name)) + { //SSE can't handle unaligned data + xor_sum.as_u32x4 = *((u32x4 *) name) & + UNION_CAST (prefix->name.ip6.as_u64[0], u32x4); + } + else +#endif /* CLIB_HAVE_VEC128 */ + { + xor_sum.as_u64[0] = ((u64 *) name)[0] & prefix->name.ip6.as_u64[0]; + xor_sum.as_u64[1] = ((u64 *) name)[1] & prefix->name.ip6.as_u64[1]; + } + + return (xor_sum.as_u64[0] == prefix->name.ip6.as_u64[0]) && + (xor_sum.as_u64[1] == prefix->name.ip6.as_u64[1]); +} + +static_always_inline u32 +hicn_face_prod_next_from_data_hdr (vlib_node_runtime_t * node, + vlib_buffer_t * b, hicn_prefix_t * prefix) +{ + u8 *ptr = vlib_buffer_get_current (b); + u8 v = *ptr & 0xf0; + int match_res = 1; + + if (PREDICT_TRUE (v == 0x40 && ip46_address_is_ip4 (&prefix->name))) + { + match_res = match_ip4_name ((u32 *) & (ptr[12]), prefix); + } + else if (PREDICT_TRUE (v == 0x60 && !ip46_address_is_ip4 (&prefix->name))) + { + match_res = match_ip6_name ((u32x4 *) & (ptr[8]), prefix); + } + + b->error = 0*(1-match_res) + match_res*(node->errors[FACE_PROD_INPUT_ERROR_NOT_SOCK_PREFIX]); + + return match_res ? HICN_FACE_PROD_NEXT_DATA_IP4 + (v == + 0x60) : + HICN_FACE_PROD_NEXT_ERROR_DROP; +} + +static_always_inline void +hicn_face_prod_trace_buffer (vlib_main_t * vm, vlib_node_runtime_t * node, + u32 swif, vlib_buffer_t * b, u32 next) +{ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (b->flags & VLIB_BUFFER_IS_TRACED))) + { + hicn_face_prod_input_trace_t *t = + vlib_add_trace (vm, node, b, sizeof (*t)); + t->next_index = next; + t->sw_if_index = swif; + } +} + +static uword +hicn_face_prod_input_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next; + hicn_face_prod_next_t next_index; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from >= 8 && n_left_to_next >= 4) + { + vlib_buffer_t *b0, *b1, *b2, *b3; + u32 bi0, bi1, bi2, bi3; + hicn_face_prod_state_t *prod_face0 = NULL; + hicn_face_prod_state_t *prod_face1 = NULL; + hicn_face_prod_state_t *prod_face2 = NULL; + hicn_face_prod_state_t *prod_face3 = NULL; + u32 next0, next1, next2, next3; + + { + vlib_buffer_t *b4, *b5, *b6, *b7; + b4 = vlib_get_buffer (vm, from[4]); + b5 = vlib_get_buffer (vm, from[5]); + b6 = vlib_get_buffer (vm, from[6]); + b7 = vlib_get_buffer (vm, from[7]); + CLIB_PREFETCH (b4, CLIB_CACHE_LINE_BYTES, STORE); + CLIB_PREFETCH (b5, CLIB_CACHE_LINE_BYTES, STORE); + CLIB_PREFETCH (b6, CLIB_CACHE_LINE_BYTES, STORE); + CLIB_PREFETCH (b7, CLIB_CACHE_LINE_BYTES, STORE); + } + + bi0 = from[0]; + bi1 = from[1]; + bi2 = from[2]; + bi3 = from[3]; + + from += 4; + n_left_from -= 4; + to_next[0] = bi0; + to_next[1] = bi1; + to_next[2] = bi2; + to_next[3] = bi3; + + to_next += 4; + n_left_to_next -= 4; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + b2 = vlib_get_buffer (vm, bi2); + b3 = vlib_get_buffer (vm, bi3); + + prod_face0 = + &face_state_vec[vnet_buffer (b0)->sw_if_index[VLIB_RX]]; + prod_face1 = + &face_state_vec[vnet_buffer (b1)->sw_if_index[VLIB_RX]]; + prod_face2 = + &face_state_vec[vnet_buffer (b2)->sw_if_index[VLIB_RX]]; + prod_face3 = + &face_state_vec[vnet_buffer (b3)->sw_if_index[VLIB_RX]]; + + next0 = + hicn_face_prod_next_from_data_hdr (node, b0, &prod_face0->prefix); + next1 = + hicn_face_prod_next_from_data_hdr (node, b1, &prod_face1->prefix); + next2 = + hicn_face_prod_next_from_data_hdr (node, b2, &prod_face2->prefix); + next3 = + hicn_face_prod_next_from_data_hdr (node, b3, &prod_face3->prefix); + stats.pkts_data_count += 4; + + /* trace */ + hicn_face_prod_trace_buffer (vm, node, + vnet_buffer (b0)->sw_if_index[VLIB_RX], + b0, next0); + hicn_face_prod_trace_buffer (vm, node, + vnet_buffer (b1)->sw_if_index[VLIB_RX], + b1, next1); + hicn_face_prod_trace_buffer (vm, node, + vnet_buffer (b2)->sw_if_index[VLIB_RX], + b2, next2); + hicn_face_prod_trace_buffer (vm, node, + vnet_buffer (b3)->sw_if_index[VLIB_RX], + b3, next3); + + /* enqueue */ + vlib_validate_buffer_enqueue_x4 (vm, node, next_index, to_next, + n_left_to_next, bi0, bi1, bi2, bi3, + next0, next1, next2, next3); + + stats.pkts_processed += 4; + + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + vlib_buffer_t *b0; + u32 bi0, swif; + hicn_face_prod_state_t *prod_face = NULL; + u32 next0; + + if (n_left_from > 1) + { + vlib_buffer_t *b1; + b1 = vlib_get_buffer (vm, from[1]); + CLIB_PREFETCH (b1, CLIB_CACHE_LINE_BYTES, STORE); + } + + bi0 = from[0]; + from += 1; + n_left_from -= 1; + to_next[0] = bi0; + to_next += 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + swif = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + prod_face = &face_state_vec[swif]; + + next0 = + hicn_face_prod_next_from_data_hdr (node, b0, &prod_face->prefix); + stats.pkts_data_count++; + + /* trace */ + hicn_face_prod_trace_buffer (vm, node, + vnet_buffer (b0)->sw_if_index[VLIB_RX], + b0, next0); + + /* enqueue */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + + stats.pkts_processed += 1; + + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, node->node_index, + HICNFWD_ERROR_PROCESSED, stats.pkts_processed); + vlib_node_increment_counter (vm, node->node_index, + HICNFWD_ERROR_INTERESTS, + stats.pkts_interest_count); + vlib_node_increment_counter (vm, node->node_index, HICNFWD_ERROR_DATAS, + stats.pkts_data_count); + + return (frame->n_vectors); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE(hicn_face_prod_input_node) = +{ + .function = hicn_face_prod_input_node_fn, + .name = "hicn-face-prod-input", + .vector_size = sizeof(u32), + .format_trace = format_face_prod_input_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(face_prod_input_error_strings), + .error_strings = face_prod_input_error_strings, + .n_next_nodes = HICN_FACE_PROD_N_NEXT, + .next_nodes = + { + [HICN_FACE_PROD_NEXT_DATA_IP4] = "hicn-face-ip4-input", + [HICN_FACE_PROD_NEXT_DATA_IP6] = "hicn-face-ip6-input", + [HICN_FACE_PROD_NEXT_ERROR_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/faces/face.c b/hicn-plugin/src/faces/face.c new file mode 100755 index 000000000..f0559bb98 --- /dev/null +++ b/hicn-plugin/src/faces/face.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "face.h" +#include "ip/face_ip.h" +#include "ip/face_ip_node.h" +#include "ip/iface_ip_node.h" +#include "ip/dpo_ip.h" +#include "udp/face_udp.h" +#include "udp/face_udp_node.h" +#include "udp/iface_udp_node.h" +#include "udp/dpo_udp.h" + +dpo_id_t *face_dpo_vec; +hicn_face_vft_t *face_vft_vec; +char **face_type_names_vec; + +hicn_face_t *hicn_dpoi_face_pool; + +dpo_type_t first_type = DPO_FIRST; + +u8 * +face_show (u8 * s, int face_id, u32 indent) +{ + s = format (s, "Faces:\n", indent); + indent += 4; + int i; + vec_foreach_index (i, face_dpo_vec) + { + s = + format (s, "%U", face_vft_vec[i].format_face, + face_dpo_vec[face_id].dpoi_index, indent); + } + + return (s); + +} + +void +register_face_type (hicn_face_type_t face_type, hicn_face_vft_t * vft, + char *name) +{ + if (first_type == DPO_FIRST) + first_type = face_type; + + int idx = face_type - first_type; + ASSERT (idx >= 0); + vec_validate (face_vft_vec, idx); + vec_validate (face_type_names_vec, idx); + + /* Copy the null char as well */ + char *name_str = (char *) malloc ((strlen (name) + 1) * sizeof (char)); + strcpy (name_str, name); + face_vft_vec[idx] = *vft; + face_type_names_vec[idx] = name_str; +} + +// Make this more flexible for future types face +void +hicn_face_module_init (vlib_main_t * vm) +{ + pool_validate (hicn_dpoi_face_pool); + + hicn_face_ip_init (vm); + hicn_iface_ip_init (vm); + hicn_face_udp_init (vm); + hicn_iface_udp_init (vm); +} + +u8 * +format_hicn_face_all (u8 * s, int n, ...) +{ + va_list ap; + va_start (ap, n); + u32 indent = va_arg (ap, u32); + + s = format (s, "Faces: %d\n", indent); + + hicn_face_t *face; + + /* *INDENT-OFF* */ + pool_foreach ( face, hicn_dpoi_face_pool, + { + hicn_face_vft_t * vft = hicn_face_get_vft(face->shared.face_type); + hicn_face_id_t face_id = hicn_dpoi_get_index(face); + s = format(s, "%U\n", vft->format_face, face_id, indent); + }); + /* *INDENT-ON* */ + + return s; +} + +hicn_face_vft_t * +hicn_face_get_vft (hicn_face_type_t face_type) +{ + int idx = face_type - first_type; + if (idx >= 0) + return &face_vft_vec[idx]; + else + return NULL; + +} + +int +hicn_face_del (hicn_face_id_t face_id) +{ + int ret = HICN_ERROR_NONE; + + if (pool_len (hicn_dpoi_face_pool) > face_id) + { + hicn_face_t *face = hicn_dpoi_get_from_idx (face_id); + if (face->shared.locks == 0) + pool_put_index (hicn_dpoi_face_pool, face_id); + else + face->shared.flags |= HICN_FACE_FLAGS_DELETED; + } + else + ret = HICN_ERROR_FACE_NOT_FOUND; + + return ret; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/faces/face.h b/hicn-plugin/src/faces/face.h new file mode 100755 index 000000000..2774d9a2e --- /dev/null +++ b/hicn-plugin/src/faces/face.h @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_FACE_H__ +#define __HICN_FACE_H__ + +#include +#include +#include +#include + +typedef u8 hicn_face_flags_t; +typedef index_t hicn_face_id_t; +typedef dpo_type_t hicn_face_type_t; + +/** + * @file + * + * @brief Face + * + * This file implements a general face type. A face is carried through nodes as a + * dpo. The face state (hicn_face_t) is the object pointed by the + * dpoi_index in the dpo_id_t (see + * https://docs.fd.io/vpp/18.07/d0/d37/dpo_8h_source.html). + * A face state that does not contain the indication of the l2 adjacency is an + * incomplete face (iface), otherwise it is considered to be complete. Each face type + * provide specific node for processing packets in input or output of complete + * and incomplete faces. + */ + +/** + * @brief Fields shared among all the different types of faces + */ +typedef struct __attribute__ ((packed)) hicn_face_shared_s +{ + /* Flags to idenfity if the face is incomplete (iface), complete (face) */ + /* And a network or application face (1B) */ + hicn_face_flags_t flags; + + /* Path label (2B) */ + u16 pl_id; + + /* Number of dpo holding a reference to the dpoi (4B) */ + u32 locks; + + /* Adjacency for the neighbor (4B) */ + adj_index_t adj; + + /* local interface for the local ip address */ + u32 sw_if; + + /* Face id corresponding to the global face pool (4B) */ + union + { + hicn_face_type_t face_type; + u32 int_face_type; //To forse the face_type_t to be 4B + }; + +} hicn_face_shared_t; + +/** + * @brief Structure holding the face state. It containes the fields shared among + * all the types of faces as well it leaves some space for storing additional + * information specific to each type. + */ +typedef struct __attribute__ ((packed)) hicn_face_s +{ + /* Additional space to fill with face_type specific information */ + u8 data[2 * CLIB_CACHE_LINE_BYTES - sizeof (hicn_face_shared_t)]; + hicn_face_shared_t shared; + +} + +hicn_face_t; + +/* Pool of faces */ +extern hicn_face_t *hicn_dpoi_face_pool; + +/* Flags */ +/* A face is complete and it stores all the information. A iface lacks of the + adj index, therefore sending a packet through a iface require a lookup in + the FIB. */ +#define HICN_FACE_FLAGS_DEFAULT 0x00 +#define HICN_FACE_FLAGS_FACE 0x01 +#define HICN_FACE_FLAGS_IFACE 0x02 +#define HICN_FACE_FLAGS_APPFACE_PROD 0x04 /* Currently only IP face can be appface */ +#define HICN_FACE_FLAGS_APPFACE_CONS 0x08 /* Currently only IP face can be appface */ +#define HICN_FACE_FLAGS_DELETED 0x10 + +#define HICN_FACE_NULL (hicn_face_id_t) ~0 + +/** + * @brief Definition of the virtual functin table for an hICN FACE DPO. + * + * An hICN dpo is a combination of a dpo context (hicn_dpo_ctx or struct that + * extends a hicn_dpo_ctx) and a strategy node. The following virtual function table + * template that glues together the fuction to interact with the context and the + * creating the dpo + */ +typedef struct hicn_face_vft_s +{ + u8 *(*format_face) (u8 * s, va_list * args); + /**< Format an hICN face dpo*/ + int (*hicn_face_del) (hicn_face_id_t face_id); + void (*hicn_face_get_dpo) (hicn_face_t * face, dpo_id_t * dpo); +} hicn_face_vft_t; + + +/* Vector maintaining a dpo per face */ +extern dpo_id_t *face_dpo_vec; +extern hicn_face_vft_t *face_vft_vec; + +/* Vector holding the set of face names */ +extern char **face_type_names_vec; + +/* First face type registered in the sytem.*/ +extern dpo_type_t first_type; + +/** + * @brief Return the face id from the face state + * + * @param Pointer to the face state + * @return face id + */ +always_inline hicn_face_id_t +hicn_dpoi_get_index (hicn_face_t * face_dpoi) +{ + return face_dpoi - hicn_dpoi_face_pool; +} + +/** + * @brief Return the face from the face id. Face id must be valid. + * + * @param dpoi_index Face identifier + * @return Pointer to the face + */ +always_inline hicn_face_t * +hicn_dpoi_get_from_idx (hicn_face_id_t dpoi_index) +{ + return (hicn_face_t *) pool_elt_at_index (hicn_dpoi_face_pool, dpoi_index); +} + +/** + * @brief Return true if the face id belongs to an existing face + */ +always_inline int +hicn_dpoi_idx_is_valid (hicn_face_id_t face_id) +{ + return pool_len (hicn_dpoi_face_pool) > face_id + && !pool_is_free_index (hicn_dpoi_face_pool, face_id); +} + +/** + * @brief Add a lock to the face dpo + * + * @param dpo Pointer to the face dpo + */ +always_inline void +hicn_face_lock (dpo_id_t * dpo) +{ + hicn_face_t *face; + face = hicn_dpoi_get_from_idx (dpo->dpoi_index); + face->shared.locks++; +} + +/** + * @brief Remove a lock to the face dpo. Deallocate the face id locks == 0 + * + * @param dpo Pointer to the face dpo + */ +always_inline void +hicn_face_unlock (dpo_id_t * dpo) +{ + hicn_face_t *face; + face = hicn_dpoi_get_from_idx (dpo->dpoi_index); + face->shared.locks--; +} + +/** + * @brief Init the internal structures of the face module + * + * Must be called before processing any packet + */ +void hicn_face_module_init (vlib_main_t * vm); + +/** + * @brief Format all the existing faces + * + * @param s Pointer to a previous string. If null it will be initialize + * @param n Number of input parameters + * @return String with the faces formatted + */ +u8 *format_hicn_face_all (u8 * s, int n, ...); + +/** + * @brief Delete a face + * + * @param face_id Id of the face to delete + * @return HICN_ERROR_FACE_NOT_FOUND if the face does not exist, otherwise + * HICN_ERROR_NONE + */ +int hicn_face_del (hicn_face_id_t face_id); + +/** + * @brief Return the virtual function table corresponding to the face type + * + * @param face_type Type of the face + * @return NULL if the face type does not exist + */ +hicn_face_vft_t *hicn_face_get_vft (hicn_face_type_t face_type); + +/** + * @brief Register a new face type + * + * @param face_type Type of the face + * @param vft Virtual Function table for the new face type + */ +void register_face_type (hicn_face_type_t face_type, hicn_face_vft_t * vft, + char *name); +#endif // __HICN_FACE_H__ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/faces/face_cli.c b/hicn-plugin/src/faces/face_cli.c new file mode 100755 index 000000000..3ddf96beb --- /dev/null +++ b/hicn-plugin/src/faces/face_cli.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "face.h" +#include "../error.h" + +static clib_error_t * +hicn_face_cli_show_command_fn (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + + hicn_face_id_t face_id = HICN_FACE_NULL; + char *face_type_name = NULL; + int found = ~0; + int deleted = 0; + + + /* Get a line of input. */ + unformat_input_t _line_input, *line_input = &_line_input; + if (unformat_user (main_input, unformat_line_input, line_input)) + { + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "%u", &face_id)) + ; + else if (unformat (line_input, "type %s", &face_type_name)) + ; + else if (unformat (line_input, "deleted")) + deleted = 1; + else + { + return clib_error_return (0, "%s", + get_error_string + (HICN_ERROR_CLI_INVAL)); + } + } + + if (face_type_name != NULL) + { + int idx = 0; + vec_foreach_index (idx, face_type_names_vec) + { + if (!strcmp (face_type_names_vec[idx], face_type_name)) + found = idx; + } + if (found == ~0) + return (clib_error_return (0, "Face type unknown")); + } + + } + + if (face_id != HICN_FACE_NULL) + { + if (!hicn_dpoi_idx_is_valid (face_id)) + return clib_error_return (0, "%s", + get_error_string + (HICN_ERROR_FACE_NOT_FOUND)); + + hicn_face_t *face = hicn_dpoi_get_from_idx (face_id); + hicn_face_vft_t *vft = hicn_face_get_vft (face->shared.face_type); + vlib_cli_output (vm, "%U\n", vft->format_face, face_id, 0 /*indent */ ); + } + else + { + if (found != ~0) + { + hicn_face_t *face; + dpo_type_t type = (dpo_type_t) (found + first_type); + hicn_face_vft_t *vft = hicn_face_get_vft (type); + /* *INDENT-OFF* */ + pool_foreach(face, hicn_dpoi_face_pool, + { + if (!((face->shared.flags & HICN_FACE_FLAGS_DELETED) && !deleted)) + { + if ((face->shared.face_type == type) && (face->shared.flags)) + vlib_cli_output(vm, "%U\n", vft->format_face, hicn_dpoi_get_index(face), 0); + } + }); + /* *INDENT-ON* */ + } + else + { + hicn_face_t *face; + /* *INDENT-OFF* */ + pool_foreach(face, hicn_dpoi_face_pool, + { + if (!((face->shared.flags & HICN_FACE_FLAGS_DELETED) && !deleted)) + { + hicn_face_vft_t * vft = hicn_face_get_vft(face->shared.face_type); + vlib_cli_output(vm, "%U\n", vft->format_face, hicn_dpoi_get_index(face), 0); + } + }); + /* *INDENT-ON* */ + } + } + + return 0; +} + +/* cli declaration for 'show faces' */ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (hicn_face_cli_show_command, static) = +{ + .path = "hicn face show", + .short_help = "hicn face show [| type ]", + .function = hicn_face_cli_show_command_fn, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/faces/ip/dpo_ip.c b/hicn-plugin/src/faces/ip/dpo_ip.c new file mode 100755 index 000000000..1b2dbcff9 --- /dev/null +++ b/hicn-plugin/src/faces/ip/dpo_ip.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "dpo_ip.h" + +mhash_t hicn_face_ip_local_hashtb; +mhash_t hicn_face_ip_remote_hashtb; +dpo_type_t hicn_face_ip_type; + +const static char *const hicn_face_ip4dpoi_nodes[] = { + "hicn-face-ip4-input", + "hicn-face-ip4-output", + "hicn-iface-ip4-input", + "hicn-iface-ip4-output", + NULL, +}; + +const static char *const hicn_face_ip6dpoi_nodes[] = { + "hicn-face-ip6-input", + "hicn-face-ip6-output", + "hicn-iface-ip6-input", + "hicn-iface-ip6-output", + NULL, +}; + +const static char *const *const hicn_ip_nodes[DPO_PROTO_NUM] = { + [DPO_PROTO_IP4] = hicn_face_ip4dpoi_nodes, + [DPO_PROTO_IP6] = hicn_face_ip6dpoi_nodes +}; + +const static dpo_vft_t hicn_face_ip_vft = { + .dv_lock = hicn_face_lock, + .dv_unlock = hicn_face_unlock, + .dv_format = format_hicn_face_ip, +}; + +/* Must be executed after all the strategy nodes are created */ +void +hicn_dpo_ip_module_init (void) +{ + mhash_init (&hicn_face_ip_local_hashtb, + sizeof (hicn_face_id_t) /* value */ , + sizeof (hicn_face_ip_key_t) /* key */ ); + mhash_init (&hicn_face_ip_remote_hashtb, + sizeof (hicn_face_id_t) /* value */ , + sizeof (hicn_face_ip_key_t) /* key */ ); + + /* + * How much useful is the following registration? + * So far it seems that we need it only for setting the dpo_type. + */ + hicn_face_ip_type = + dpo_register_new_type (&hicn_face_ip_vft, hicn_ip_nodes); +} + + +int +hicn_dpo_ip4_create (dpo_id_t * dpo, + const ip4_address_t * local_addr, + const ip4_address_t * remote_addr, + u32 sw_if, + adj_index_t adj, + u32 node_index, + hicn_face_flags_t flags, hicn_face_id_t * face_id) +{ + /* If local matches the dpoi is a face */ + hicn_face_t *face = + hicn_face_ip4_get (local_addr, sw_if, &hicn_face_ip_local_hashtb); + u8 is_appface; + + if (face != NULL) + return HICN_ERROR_FACE_ALREADY_CREATED; + + face = hicn_face_ip4_get (remote_addr, sw_if, &hicn_face_ip_remote_hashtb); + + if (face == NULL) + { + hicn_dpo_ip4_add_and_lock_from_remote (dpo, &is_appface, local_addr, + remote_addr, sw_if, node_index); + *face_id = (hicn_face_id_t) dpo->dpoi_index; + face = hicn_dpoi_get_from_idx (*face_id); + } + else + { + *face_id = hicn_dpoi_get_index (face); + dpo_set (dpo, hicn_face_ip_type, DPO_PROTO_IP4, *face_id); + dpo->dpoi_next_node = node_index; + } + + + hicn_face_ip_key_t key; + hicn_face_ip4_get_key (local_addr, sw_if, &key); + + mhash_set_mem (&hicn_face_ip_local_hashtb, &key, (uword *) face_id, 0); + + hicn_face_ip_t *ip_face = (hicn_face_ip_t *) face->data; + ip46_address_set_ip4 (&ip_face->local_addr, local_addr); + ip46_address_set_ip4 (&ip_face->remote_addr, remote_addr); + face->shared.flags = flags; + face->shared.adj = adj; + + return HICN_ERROR_NONE; +} + +int +hicn_dpo_ip6_create (dpo_id_t * dpo, + const ip6_address_t * local_addr, + const ip6_address_t * remote_addr, + u32 sw_if, + adj_index_t adj, + u32 node_index, + hicn_face_flags_t flags, hicn_face_id_t * face_id) +{ + /* If local matches the dpoi is a face */ + hicn_face_t *face = + hicn_face_ip6_get (local_addr, sw_if, &hicn_face_ip_local_hashtb); + + u8 is_appface; + + if (face != NULL) + return HICN_ERROR_FACE_ALREADY_CREATED; + + face = hicn_face_ip6_get (remote_addr, sw_if, &hicn_face_ip_remote_hashtb); + + /* If remote matches the dpoi is a iface */ + if (face == NULL) + { + hicn_dpo_ip6_add_and_lock_from_remote (dpo, &is_appface, local_addr, + remote_addr, sw_if, node_index); + *face_id = (hicn_face_id_t) dpo->dpoi_index; + face = hicn_dpoi_get_from_idx (*face_id); + } + else + { + *face_id = hicn_dpoi_get_index (face); + dpo_set (dpo, hicn_face_ip_type, DPO_PROTO_IP6, *face_id); + dpo->dpoi_next_node = node_index; + } + + hicn_face_ip_key_t key; + hicn_face_ip6_get_key (local_addr, sw_if, &key); + + mhash_set_mem (&hicn_face_ip_local_hashtb, &key, (uword *) face_id, 0); + + hicn_face_ip_t *ip_face = (hicn_face_ip_t *) face->data; + clib_memcpy (&ip_face->local_addr, local_addr, sizeof (ip6_address_t)); + clib_memcpy (&ip_face->remote_addr, remote_addr, sizeof (ip6_address_t)); + face->shared.sw_if = sw_if; + face->shared.flags = flags; + face->shared.adj = adj; + + + return HICN_ERROR_NONE; +} + +void +hicn_dpo_ip_create_from_face (hicn_face_t * face, dpo_id_t * dpo, + u16 dpoi_next_node) +{ + hicn_face_id_t face_dpoi_id = hicn_dpoi_get_index (face); + hicn_face_ip_t *ip_face = (hicn_face_ip_t *) face->data; + dpo_set (dpo, face->shared.face_type, + ip46_address_is_ip4 (&ip_face-> + local_addr) ? DPO_PROTO_IP4 : DPO_PROTO_IP6, + face_dpoi_id); + dpo->dpoi_next_node = dpoi_next_node; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/faces/ip/dpo_ip.h b/hicn-plugin/src/faces/ip/dpo_ip.h new file mode 100755 index 000000000..675443277 --- /dev/null +++ b/hicn-plugin/src/faces/ip/dpo_ip.h @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_DPO_IP_H__ +#define __HICN_DPO_IP_H__ + +#include +#include + +#include "face_ip.h" +#include "../face.h" + +/** + * @brief Initialize the internal structures of the dpo ip face module. + */ +void hicn_dpo_ip_module_init (void); + + +/** + * @brief Retrieve a face from the ip4 local address and returns its dpo. This + * method adds a lock on the face state. + * + * @param dpo: Result of the lookup. If the face doesn't exist dpo = NULL + * @param is_appface: Boolean that indicates whether the face is an application + * face or not + * @param local_addr: Ip v4 local address of the face + * @param sw_if: software interface id of the face + * + * @result HICN_ERROR_FACE_NOT_FOUND if the face does not exist, otherwise HICN_ERROR_NONE. + */ +always_inline int +hicn_dpo_ip4_lock_from_local (dpo_id_t * dpo, + u8 * is_appface, + const ip4_address_t * local_addr, u32 sw_if) +{ + hicn_face_t *face = + hicn_face_ip4_get (local_addr, sw_if, &hicn_face_ip_local_hashtb); + + if (PREDICT_FALSE (face == NULL)) + return HICN_ERROR_FACE_NOT_FOUND; + + *is_appface = face->shared.flags & HICN_FACE_FLAGS_APPFACE_PROD; + + hicn_face_id_t dpoi_index = hicn_dpoi_get_index (face); + dpo_set (dpo, hicn_face_ip_type, DPO_PROTO_IP4, dpoi_index); + dpo->dpoi_next_node = ~0; + dpo_lock (dpo); + + return HICN_ERROR_NONE; +} + +/** + * @brief Retrieve a face from the ip6 local address and returns its dpo. This + * method adds a lock on the face state. + * + * @param dpo: Result of the lookup. If the face doesn't exist dpo = NULL + * @param is_appface: Boolean that indicates whether the face is an application + * face or not + * @param local_addr: Ip v6 local address of the face + * @param sw_if: software interface id of the face + * + * @result HICN_ERROR_FACE_NOT_FOUND if the face does not exist, otherwise HICN_ERROR_NONE. + */ +always_inline int +hicn_dpo_ip6_lock_from_local (dpo_id_t * dpo, + u8 * is_appface, + const ip6_address_t * local_addr, u32 sw_if) +{ + hicn_face_t *face = + hicn_face_ip6_get (local_addr, sw_if, &hicn_face_ip_local_hashtb); + + if (PREDICT_FALSE (face == NULL)) + return HICN_ERROR_FACE_NOT_FOUND; + + *is_appface = face->shared.flags & HICN_FACE_FLAGS_APPFACE_PROD; + + hicn_face_id_t dpoi_index = hicn_dpoi_get_index (face); + dpo_set (dpo, hicn_face_ip_type, DPO_PROTO_IP6, dpoi_index); + dpo->dpoi_next_node = ~0; + dpo_lock (dpo); + + return HICN_ERROR_NONE; +} + + +/** + * @brief Retrieve, or create if it doesn't exist, a face from the ip6 local + * address and returns its dpo. This method adds a lock on the face state. + * + * @param dpo: Result of the lookup + * @param is_appface: Boolean that indicates whether the face is an application + * face or not + * @param local_addr: Ip v4 local address of the face + * @param remote_addr: Ip v4 remote address of the face + * @param sw_if: software interface id of the face + * @param node_index: vlib edge index to use in the packet processing + */ +always_inline void +hicn_dpo_ip4_add_and_lock_from_remote (dpo_id_t * dpo, + u8 * is_appface, + const ip4_address_t * local_addr, + const ip4_address_t * remote_addr, + u32 sw_if, u32 node_index) +{ + /*All (complete) faces are indexed by remote addess as well */ + hicn_face_t *face = + hicn_face_ip4_get (remote_addr, sw_if, &hicn_face_ip_remote_hashtb); + + if (face == NULL) + { + hicn_face_id_t dpoi_index; + ip46_address_t local_addr46 = to_ip46 (0, (u8 *) local_addr); + ip46_address_t remote_addr46 = to_ip46 (0, (u8 *) remote_addr); + hicn_iface_ip_add (&local_addr46, &remote_addr46, sw_if, &dpoi_index); + + *is_appface = 0; + + dpo_set (dpo, hicn_face_ip_type, DPO_PROTO_IP4, dpoi_index); + dpo->dpoi_next_node = node_index; + dpo_lock (dpo); + + return; + } + + /* Code replicated on purpose */ + *is_appface = face->shared.flags & HICN_FACE_FLAGS_APPFACE_PROD; + + hicn_face_id_t dpoi_index = hicn_dpoi_get_index (face); + dpo_set (dpo, hicn_face_ip_type, DPO_PROTO_IP4, dpoi_index); + dpo->dpoi_next_node = node_index; + dpo_lock (dpo); +} + +/** + * @brief Retrieve, or create if it doesn't exist, a face from the ip6 local + * address and returns its dpo. This method adds a lock on the face state. + * + * @param dpo: Result of the lookup + * @param is_appface: Boolean that indicates whether the face is an application + * face or not + * @param local_addr: Ip v6 local address of the face + * @param remote_addr: Ip v6 remote address of the face + * @param sw_if: software interface id of the face + * @param node_index: vlib edge index to use in the packet processing + */ +always_inline void +hicn_dpo_ip6_add_and_lock_from_remote (dpo_id_t * dpo, + u8 * is_appface, + const ip6_address_t * local_addr, + const ip6_address_t * remote_addr, + u32 sw_if, u32 node_index) +{ + /*All (complete) faces are indexed by remote addess as well */ + hicn_face_t *face = + hicn_face_ip6_get (remote_addr, sw_if, &hicn_face_ip_remote_hashtb); + + if (face == NULL) + { + hicn_face_id_t dpoi_index; + hicn_iface_ip_add ((ip46_address_t *) local_addr, + (ip46_address_t *) remote_addr, sw_if, &dpoi_index); + + *is_appface = 0; + + dpo_set (dpo, hicn_face_ip_type, DPO_PROTO_IP4, dpoi_index); + dpo->dpoi_next_node = node_index; + dpo_lock (dpo); + + return; + } + /* Code replicated on purpose */ + *is_appface = face->shared.flags & HICN_FACE_FLAGS_APPFACE_PROD; + + index_t dpoi_index = hicn_dpoi_get_index (face); + dpo_set (dpo, hicn_face_ip_type, DPO_PROTO_IP6, dpoi_index); + dpo->dpoi_next_node = node_index; + dpo_lock (dpo); +} + + +/** + * @brief Create an ip face and its corresponding dpo. Meant to be used for the + * control plane. + * + * @param dpo: Data plane object that point to the face created. + * @param local_addr: Ip v4 local address of the face + * @param remote_addr: Ip v4 remote address of the face + * @param sw_if: software interface id of the face + * @param adj: Ip adjacency corresponding to the remote address in the face + * @param node_index: vlib edge index to use in the packet processing + * @param flags: Flags of the face + * @param face_id: Identifier for the face (dpoi_index) + * @return HICN_ERROR_FACE_ALREADY_CREATED if the face exists, otherwise HICN_ERROR_NONE + */ +int hicn_dpo_ip4_create (dpo_id_t * dpo, + const ip4_address_t * local_addr, + const ip4_address_t * remote_addr, + u32 sw_if, + adj_index_t adj, + u32 node_index, + hicn_face_flags_t flags, hicn_face_id_t * face_id); + +/** + * @brief Create an ip face and its corresponding dpo. Meant to be used for the + * control plane. + * + * @param dpo: Data plane object that point to the face created. + * @param local_addr: Ip v6 local address of the face + * @param remote_addr: Ip v6 remote address of the face + * @param sw_if: software interface id of the face + * @param adj: Ip adjacency corresponding to the remote address in the face + * @param node_index: vlib edge index to use in the packet processing + * @param flags: Flags of the face + * @param face_id: Identifier for the face (dpoi_index) + * @return HICN_ERROR_FACE_ALREADY_CREATED if the face exists, otherwise HICN_ERROR_NONE + */ +int hicn_dpo_ip6_create (dpo_id_t * dpo, + const ip6_address_t * local_addr, + const ip6_address_t * remote_addr, + u32 sw_if, + adj_index_t adj, + u32 node_index, + hicn_face_flags_t flags, hicn_face_id_t * face_id); + +/** + * @brief Create a dpo from an ip face + * + * @param face Face from which to create the dpo + * @param dpoi_next_node Edge index that connects a node to the iface or face nodes + * @return the dpo + */ +void hicn_dpo_ip_create_from_face (hicn_face_t * face, dpo_id_t * dpo, + u16 dpoi_next_node); + +#endif // __HICN_DPO_IP_H__ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/faces/ip/face_ip.c b/hicn-plugin/src/faces/ip/face_ip.c new file mode 100755 index 000000000..c7f6a1ba1 --- /dev/null +++ b/hicn-plugin/src/faces/ip/face_ip.c @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "face_ip.h" +#include "face_ip_node.h" +#include "dpo_ip.h" +#include "../../strategy_dpo_manager.h" +#include "../face.h" +#include "../../cache_policies/cs_lru.h" +#include "../../infra.h" +#include "../../hicn.h" +#include "../app/face_prod.h" +#include "../app/face_cons.h" + +#include "../../mapme.h" // HICN_MAPME_EVENT_* +#include "../../mapme_eventmgr.h" // hicn_mapme_eventmgr_process_node + +extern vlib_node_registration_t hicn_mapme_eventmgr_process_node; + +u32 strategy_face_ip4_vlib_edge; +u32 strategy_face_ip6_vlib_edge; + +void +hicn_face_ip_init (vlib_main_t * vm) +{ + int strategy_nodes_n = hicn_strategy_get_all_available (); + + /* Default Strategy has index 0 and it always exists */ + strategy_face_ip4_vlib_edge = vlib_node_add_next (vm, + hicn_dpo_get_strategy_vft + (default_dpo. + hicn_dpo_get_type ())-> + get_strategy_node_index + (), + hicn_face_ip4_output_node. + index); + + strategy_face_ip6_vlib_edge = vlib_node_add_next (vm, + hicn_dpo_get_strategy_vft + (default_dpo. + hicn_dpo_get_type ())-> + get_strategy_node_index + (), + hicn_face_ip6_output_node. + index); + /* + * Create and edge between al the other strategy nodes + * and the ip_encap nodes. + */ + for (int i = 1; i < strategy_nodes_n; i++) + { + u32 temp_index4 = vlib_node_add_next (vm, + hicn_dpo_get_strategy_vft_from_id + (i)->get_strategy_node_index (), + hicn_face_ip4_output_node.index); + u32 temp_index6 = vlib_node_add_next (vm, + hicn_dpo_get_strategy_vft_from_id + (i)->get_strategy_node_index (), + hicn_face_ip6_output_node.index); + ASSERT (temp_index4 == strategy_face_ip4_vlib_edge); + ASSERT (temp_index6 == strategy_face_ip6_vlib_edge); + } + + hicn_dpo_ip_module_init (); + + register_face_type (hicn_face_ip_type, &ip_vft, "ip"); +} + +int +hicn_face_ip_del (hicn_face_id_t face_id) +{ + hicn_face_t *face = hicn_dpoi_get_from_idx (face_id); + hicn_face_ip_t *face_ip = (hicn_face_ip_t *) face->data; + hicn_face_ip_key_t key; + hicn_face_ip_key_t old_key; + + if (ip46_address_is_ip4 (&face_ip->local_addr)) + { + hicn_face_ip4_get_key (&(face_ip->local_addr.ip4), face->shared.sw_if, + &key); + mhash_unset (&hicn_face_ip_local_hashtb, &key, (uword *) & old_key); + hicn_face_ip4_get_key (&(face_ip->remote_addr.ip4), face->shared.sw_if, + &key); + mhash_unset (&hicn_face_ip_remote_hashtb, &key, (uword *) & old_key); + } + else + { + hicn_face_ip6_get_key (&(face_ip->local_addr.ip6), face->shared.sw_if, + &key); + mhash_unset (&hicn_face_ip_local_hashtb, &key, (uword *) & old_key); + hicn_face_ip6_get_key (&(face_ip->remote_addr.ip6), face->shared.sw_if, + &key); + mhash_unset (&hicn_face_ip_remote_hashtb, &key, (uword *) & old_key); + } + return hicn_face_del (face_id); +} + + +/* + * Utility that adds a new face cache entry. For the moment we assume that the + * ip_adjacency has already been set up. + */ +int +hicn_face_ip_add (const ip46_address_t * local_addr, + const ip46_address_t * remote_addr, + int sw_if, hicn_face_id_t * pfaceid) +{ + fib_protocol_t fib_type; + vnet_link_t link_type; + adj_index_t adj; + dpo_proto_t dpo_proto; + + /* Check if we found at least one ip address */ + if (ip46_address_is_zero (local_addr) || ip46_address_is_zero (remote_addr)) + return HICN_ERROR_FACE_NO_GLOBAL_IP; + + if (ip46_address_is_ip4 (local_addr) && ip46_address_is_ip4 (remote_addr)) + { + link_type = VNET_LINK_IP4; + fib_type = FIB_PROTOCOL_IP4; + } + else + { + link_type = VNET_LINK_IP6; + fib_type = FIB_PROTOCOL_IP6; + } + + + adj = adj_nbr_add_or_lock (fib_type, link_type, remote_addr, sw_if); + + hicn_face_flags_t flags = (hicn_face_flags_t) 0; + flags |= HICN_FACE_FLAGS_FACE; + + hicn_face_t *face; + if (ip46_address_is_ip4 (local_addr)) + { + face = + hicn_face_ip4_get (&(local_addr->ip4), sw_if, + &hicn_face_ip_local_hashtb); + + if (face != NULL) + return HICN_ERROR_FACE_ALREADY_CREATED; + + face = + hicn_face_ip4_get (&(remote_addr->ip4), sw_if, + &hicn_face_ip_remote_hashtb); + + /* If remote matches the face is a iface */ + if (face == NULL) + { + hicn_iface_ip_add (local_addr, remote_addr, sw_if, pfaceid); + face = hicn_dpoi_get_from_idx (*pfaceid); + } + else + { + *pfaceid = hicn_dpoi_get_index (face); + } + + hicn_face_ip_key_t key; + hicn_face_ip4_get_key (&(local_addr->ip4), sw_if, &key); + + mhash_set_mem (&hicn_face_ip_local_hashtb, &key, (uword *) pfaceid, 0); + + hicn_face_ip_t *ip_face = (hicn_face_ip_t *) face->data; + clib_memcpy (&ip_face->local_addr, local_addr, sizeof (ip4_address_t)); + clib_memcpy (&ip_face->remote_addr, remote_addr, + sizeof (ip4_address_t)); + face->shared.sw_if = sw_if; + face->shared.flags = flags; + face->shared.adj = adj; + + dpo_proto = DPO_PROTO_IP4; + } + else + { + face = + hicn_face_ip6_get (&(local_addr->ip6), sw_if, + &hicn_face_ip_local_hashtb); + + if (face != NULL) + return HICN_ERROR_FACE_ALREADY_CREATED; + + face = + hicn_face_ip6_get (&(remote_addr->ip6), sw_if, + &hicn_face_ip_remote_hashtb); + + /* If remote matches the face is a iface */ + if (face == NULL) + { + hicn_iface_ip_add (local_addr, remote_addr, sw_if, pfaceid); + face = hicn_dpoi_get_from_idx (*pfaceid); + } + else + { + *pfaceid = hicn_dpoi_get_index (face); + } + + hicn_face_ip_key_t key; + hicn_face_ip6_get_key (&(local_addr->ip6), sw_if, &key); + + mhash_set_mem (&hicn_face_ip_local_hashtb, &key, (uword *) pfaceid, 0); + + hicn_face_ip_t *ip_face = (hicn_face_ip_t *) face->data; + clib_memcpy (&ip_face->local_addr, local_addr, sizeof (ip6_address_t)); + clib_memcpy (&ip_face->remote_addr, remote_addr, + sizeof (ip6_address_t)); + face->shared.sw_if = sw_if; + face->shared.flags = flags; + face->shared.adj = adj; + + dpo_proto = DPO_PROTO_IP6; + } + + retx_t *retx = vlib_process_signal_event_data (vlib_get_main (), + hicn_mapme_eventmgr_process_node. + index, + HICN_MAPME_EVENT_FACE_ADD, 1, + sizeof (retx_t)); + *retx = (retx_t) + { + .prefix = 0,.dpo = (dpo_id_t) + { + .dpoi_type = hicn_face_ip_type,.dpoi_proto = dpo_proto,.dpoi_next_node = + 0,.dpoi_index = *pfaceid,} + }; + + return HICN_ERROR_NONE; +} + +u8 * +format_hicn_face_ip (u8 * s, va_list * args) +{ + index_t index = va_arg (*args, index_t); + CLIB_UNUSED (u32 indent) = va_arg (*args, u32); + hicn_face_t *face; + hicn_face_ip_t *ip_face; + ip_adjacency_t *adj; + vnet_main_t *vnm = vnet_get_main (); + + face = hicn_dpoi_get_from_idx (index); + ip_face = (hicn_face_ip_t *) face->data; + + if (face->shared.flags & HICN_FACE_FLAGS_FACE) + { + ASSERT (face->shared.adj != (adj_index_t) ~ 0); + adj = adj_get (face->shared.adj); + + hicn_face_id_t face_id = hicn_dpoi_get_index (face); + s = format (s, "%U Face %d: ", format_white_space, indent, face_id); + s = format (s, "type IP local %U ", + format_ip46_address, &ip_face->local_addr, IP46_TYPE_ANY); + s = + format (s, "remote %U ", format_ip46_address, &ip_face->remote_addr, + IP46_TYPE_ANY); + s = format (s, "%U", format_vnet_link, adj->ia_link); + s = format (s, " dev %U", format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, face->shared.sw_if)); + + if ((face->shared.flags & HICN_FACE_FLAGS_APPFACE_PROD)) + s = format (s, " %U", format_hicn_face_prod, face_id, 0); + else if ((face->shared.flags & HICN_FACE_FLAGS_APPFACE_CONS)) + s = format (s, " %U", format_hicn_face_cons, face_id, 0); + + if ((face->shared.flags & HICN_FACE_FLAGS_DELETED)) + s = format (s, " (deleted)"); + } + else + { + hicn_face_id_t face_id = hicn_dpoi_get_index (face); + s = format (s, "%U iFace %d: ", format_white_space, indent, face_id); + s = format (s, "type IP local %U remote %U", + format_ip46_address, &ip_face->local_addr, IP46_TYPE_ANY, + format_ip46_address, &ip_face->remote_addr, IP46_TYPE_ANY); + s = + format (s, " dev %U", format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, face->shared.sw_if)); + + if ((face->shared.flags & HICN_FACE_FLAGS_APPFACE_PROD)) + s = format (s, " %U", format_hicn_face_prod, face_id, 0); + else if ((face->shared.flags & HICN_FACE_FLAGS_APPFACE_CONS)) + s = format (s, " %U", format_hicn_face_cons, face_id, 0); + + if ((face->shared.flags & HICN_FACE_FLAGS_DELETED)) + s = format (s, " (deleted)"); + } + + return s; +} + +void +hicn_face_ip_get_dpo (hicn_face_t * face, dpo_id_t * dpo) +{ + + hicn_face_ip_t *face_ip = (hicn_face_ip_t *) face->data; + return hicn_dpo_ip_create_from_face (face, dpo, + ip46_address_is_ip4 (&face_ip-> + remote_addr) ? + strategy_face_ip4_vlib_edge : + strategy_face_ip6_vlib_edge); +} + +hicn_face_vft_t ip_vft = { + .format_face = format_hicn_face_ip, + .hicn_face_del = hicn_face_ip_del, + .hicn_face_get_dpo = hicn_face_ip_get_dpo, +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/faces/ip/face_ip.h b/hicn-plugin/src/faces/ip/face_ip.h new file mode 100755 index 000000000..8c31f6dd3 --- /dev/null +++ b/hicn-plugin/src/faces/ip/face_ip.h @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_FACE_IP_H__ +#define __HICN_FACE_IP_H__ + +#include +#include +#include "../face.h" +#include "../../cache_policies/cs_policy.h" + +/** + * @file + * + * @brief IP face + * + * A face is carried through nodes as a dpo. The face state is the object + * pointed by the dpoi_index in the dpo_id_t (see + * https://docs.fd.io/vpp/18.07/d0/d37/dpo_8h_source.html) + */ +typedef struct hicn_ip_face_t_ +{ + /** + * The headers to paint, in packet painting order + */ + /* Local address of the interface sw_if */ + ip46_address_t local_addr; + + /* Remote address of neighbor */ + ip46_address_t remote_addr; + +} hicn_face_ip_t; + + +/** + * Hash tables that indexes a face by local address. For fast lookup when an + * data arrives. + */ +extern mhash_t hicn_face_ip_local_hashtb; + +/** + * Hash tables that indexes a face by remote address. For fast lookup when an + * interest arrives. + */ +extern mhash_t hicn_face_ip_remote_hashtb; + +/** + * Key definition for the mhash table. An ip face is uniquely identified by ip + * address and the interface id. The ip address can correspond to the remote ip + * address of the next hicn hop, or to the local address of the receiving + * interface. The former is used to retrieve the incoming face when an interest + * is received, the latter when the arring packet is a data. + */ +typedef struct hicn_face_ip_key_s +{ + ip46_address_t addr; + u32 sw_if; +} hicn_face_ip_key_t; + + +extern hicn_face_type_t hicn_face_ip_type; +extern hicn_face_vft_t ip_vft; + +/** + * @brief Create the key object for the mhash. Fill in the key object with the + * expected values. + * + * @param addr Local or remote ip v6 address of the face + * @param sw_if interface associated to the face + * @param key Pointer to an allocated hicn_face_ip_key_t object + */ +always_inline void +hicn_face_ip6_get_key (const ip6_address_t * addr, + u32 sw_if, hicn_face_ip_key_t * key) +{ + key->addr.ip6 = *addr; + key->sw_if = sw_if; +} + + +/** + * @brief Create the key object for the mhash. Fill in the key object with the + * expected values. + * + * @param addr Local or remote ip v4 address of the face + * @param sw_if interface associated to the face + * @param key Pointer to an allocated hicn_face_ip_key_t object + */ +always_inline void +hicn_face_ip4_get_key (const ip4_address_t * addr, + u32 sw_if, hicn_face_ip_key_t * key) +{ + ip46_address_set_ip4 (&(key->addr), addr); + key->sw_if = sw_if; +} + +/** + * @brief Get the dpoi from the ip v4 address. Does not add any lock. + * + * @param addr Ip v4 address used to create the key for the hash table. + * @param sw_if Software interface id used to create the key for the hash table. + * @param hashtb Hash table (remote or local) where to perform the lookup. + * + * @result Pointer to the face. + */ +always_inline hicn_face_t * +hicn_face_ip4_get (const ip4_address_t * addr, u32 sw_if, mhash_t * hashtb) +{ + hicn_face_ip_key_t key; + + hicn_face_ip4_get_key (addr, sw_if, &key); + + hicn_face_id_t *dpoi_index = (hicn_face_id_t *) mhash_get (hashtb, + &key); + + return dpoi_index == NULL ? NULL : hicn_dpoi_get_from_idx (*dpoi_index); +} + +/** + * @brief Get the dpoi from the ip v6 address. Does not add any lock. + * + * @param addr Ip v6 address used to create the key for the hash table. + * @param sw_if Software interface id used to create the key for the hash table. + * @param hashtb Hash table (remote or local) where to perform the lookup. + * + * @result Pointer to the face. + */ +always_inline hicn_face_t * +hicn_face_ip6_get (const ip6_address_t * addr, u32 sw_if, mhash_t * hashtb) +{ + hicn_face_ip_key_t key; + + hicn_face_ip6_get_key (addr, sw_if, &key); + + hicn_face_id_t *dpoi_index = (hicn_face_id_t *) mhash_get (hashtb, + &key); + + return dpoi_index == NULL ? NULL : hicn_dpoi_get_from_idx (*dpoi_index); +} + +/** + * @brief Create a new face ip. API for other modules (e.g., routing) + * + * @param local_addr Local ip v4 or v6 address of the face + * @param remote_addr Remote ip v4 or v6 address of the face + * @param sw_if interface associated to the face + * @param is_app_face Boolean to set the face as an application face + * @param pfaceid Pointer to return the face id + * @return HICN_ERROR_FACE_NO_GLOBAL_IP if the face does not have a globally + * reachable ip address, otherwise HICN_ERROR_NONE + */ +int hicn_face_ip_add (const ip46_address_t * local_addr, + const ip46_address_t * remote_addr, + int swif, hicn_face_id_t * pfaceid); + +/** + * @brief Create a new incomplete face ip. (Meant to be used by the data plane) + * + * @param local_addr Local ip v4 or v6 address of the face + * @param remote_addr Remote ip v4 or v6 address of the face + * @param sw_if interface associated to the face + * @param pfaceid Pointer to return the face id + * @return HICN_ERROR_FACE_NO_GLOBAL_IP if the face does not have a globally + * reachable ip address, otherwise HICN_ERROR_NONE + */ +always_inline void +hicn_iface_ip_add (const ip46_address_t * local_addr, + const ip46_address_t * remote_addr, + int sw_if, hicn_face_id_t * pfaceid) +{ + hicn_face_t *face; + pool_get (hicn_dpoi_face_pool, face); + + hicn_face_ip_t *ip_face = (hicn_face_ip_t *) (face->data); + + clib_memcpy (&(ip_face->local_addr.ip6), local_addr, + sizeof (ip6_address_t)); + clib_memcpy (&(ip_face->remote_addr.ip6), remote_addr, + sizeof (ip6_address_t)); + face->shared.sw_if = sw_if; + + face->shared.adj = ADJ_INDEX_INVALID; + face->shared.pl_id = (u16) 0; + face->shared.face_type = hicn_face_ip_type; + face->shared.flags = HICN_FACE_FLAGS_IFACE; + face->shared.locks = 0; + + hicn_face_ip_key_t key; + hicn_face_ip6_get_key (&(remote_addr->ip6), sw_if, &key); + *pfaceid = hicn_dpoi_get_index (face); + + mhash_set_mem (&hicn_face_ip_remote_hashtb, &key, (uword *) pfaceid, 0); +} + +/** + * @brief Delete an ip face + * + * @param face_id Id of the face to delete + * @return HICN_ERROR_FACE_NOT_FOUND if the face does not exist, otherwise + * HICN_ERROR_NONE + */ +int hicn_face_ip_del (hicn_face_id_t face_id); + +/** + * @brief Format a IP face + * + * @param s Pointer to a previous string. If null it will be initialize + * @param args Array storing input values. Expected u32 face_id and u32 indent + * @return String with the formatted face + */ +u8 *format_hicn_face_ip (u8 * s, va_list * args); + +/** + * @brief Create a dpo from an ip face + * + * @param face Face from which to create the dpo + * @return the dpo + */ +void hicn_face_ip_get_dpo (hicn_face_t * face, dpo_id_t * dpo); + +#endif // __HICN_FACE_IP_H__ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/faces/ip/face_ip_cli.c b/hicn-plugin/src/faces/ip/face_ip_cli.c new file mode 100755 index 000000000..1558c82cb --- /dev/null +++ b/hicn-plugin/src/faces/ip/face_ip_cli.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "face_ip.h" +#include "dpo_ip.h" +#include "../face.h" + +#define HICN_FACE_NONE 0 +#define HICN_FACE_DELETE 1 +#define HICN_FACE_ADD 2 + +static clib_error_t * +hicn_face_ip_cli_set_command_fn (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + vnet_main_t *vnm = vnet_get_main (); + ip46_address_t local_addr; + ip46_address_t remote_addr; + hicn_face_id_t face_id = HICN_FACE_NULL; + int app_face = 0; + u32 cs_reserved = HICN_PARAM_FACE_DFT_CS_RESERVED; + int ret = HICN_ERROR_NONE; + int sw_if; + int face_op = HICN_FACE_NONE; + + ip46_address_reset (&local_addr); + /* Get a line of input. */ + unformat_input_t _line_input, *line_input = &_line_input; + if (!unformat_user (main_input, unformat_line_input, line_input)) + { + return (0); + } + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "del")) + { + if (unformat (line_input, "id %d", &face_id)) + face_op = HICN_FACE_DELETE; + else + { + return clib_error_return (0, "missing face id"); + } + } + else if (unformat (line_input, "add")) + { + face_op = HICN_FACE_ADD; + if (unformat (line_input, "local %U remote %U intfc %U", + unformat_ip46_address, &local_addr, IP46_TYPE_ANY, + unformat_ip46_address, &remote_addr, IP46_TYPE_ANY, + unformat_vnet_sw_interface, vnm, &sw_if)); + else + { + return clib_error_return (0, "%s '%U'", + get_error_string + (HICN_ERROR_CLI_INVAL), + format_unformat_error, line_input); + } + } + else if (unformat (line_input, "app_face %d", &app_face)) + { + if (unformat (line_input, "cs_size %d", &cs_reserved)); + } + else + { + return clib_error_return (0, "%s '%U'", + get_error_string (HICN_ERROR_CLI_INVAL), + format_unformat_error, line_input); + } + } + + if (face_id != HICN_FACE_NULL) + { + + if (!hicn_dpoi_idx_is_valid (face_id)) + { + return clib_error_return (0, "%s, face_id %d not valid", + get_error_string (ret), face_id); + } + } + + int rv; + switch (face_op) + { + case HICN_FACE_ADD: + + /* Check for presence of next hop address */ + if ((remote_addr.as_u64[0] == (u64) 0) + && (remote_addr.as_u64[1] == (u64) 0)) + { + return clib_error_return (0, "next hop address not specified"); + } + + rv = hicn_face_ip_add (&local_addr, &remote_addr, sw_if, &face_id); + + if (rv == HICN_ERROR_NONE) + { + vlib_cli_output (vm, "Face id: %d", face_id); + } + else + { + return clib_error_return (0, get_error_string (rv)); + } + break; + case HICN_FACE_DELETE: + rv = hicn_face_ip_del (face_id); + if (rv == HICN_ERROR_NONE) + { + vlib_cli_output (vm, "Face %d deleted", face_id); + } + else + { + return clib_error_return (0, get_error_string (rv)); + } + break; + default: + return clib_error_return (0, "Operation (%d) not implemented", face_op); + break; + } + return (rv == HICN_ERROR_NONE) ? 0 : clib_error_return (0, "%s\n", + get_error_string + (rv)); +} + +/* cli declaration for 'cfg face' */ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (hicn_face_ip_cli_set_command, static) = +{ + .path = "hicn face ip", + .short_help = "hicn face ip {add local remote intfc } {app_face <0/1>} {cs_size } | {del id }", + .function = hicn_face_ip_cli_set_command_fn, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/faces/ip/face_ip_node.c b/hicn-plugin/src/faces/ip/face_ip_node.c new file mode 100755 index 000000000..6081e4737 --- /dev/null +++ b/hicn-plugin/src/faces/ip/face_ip_node.c @@ -0,0 +1,761 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "face_ip.h" +#include "face_ip_node.h" +#include "dpo_ip.h" +#include "../../strategy_dpo_manager.h" +#include "../face.h" +#include "../../cache_policies/cs_lru.h" +#include "../../infra.h" +#include "../../hicn.h" + +/** + * @File + * + * Definition of the nodes for ip incomplete faces. + */ + +vlib_node_registration_t hicn_face_ip4_input_node; +vlib_node_registration_t hicn_face_ip4_output_node; +vlib_node_registration_t hicn_face_ip6_input_node; +vlib_node_registration_t hicn_face_ip6_output_node; + +#define ip_v4 4 +#define ip_v6 6 + +static char *hicn_face_ip4_input_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + +static char *hicn_face_ip6_input_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; +} hicn_face_ip4_input_trace_t; + +typedef enum +{ + HICN_FACE_IP4_INPUT_NEXT_DATA, + HICN_FACE_IP4_INPUT_NEXT_MAPME, + HICN_FACE_IP4_INPUT_NEXT_ERROR_DROP, + HICN_FACE_IP4_INPUT_N_NEXT, +} hicn_face_ip4_input_next_t; + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; +} hicn_face_ip6_input_trace_t; + +typedef enum +{ + HICN_FACE_IP6_INPUT_NEXT_DATA, + HICN_FACE_IP6_INPUT_NEXT_MAPME, + HICN_FACE_IP6_INPUT_NEXT_ERROR_DROP, + HICN_FACE_IP6_INPUT_N_NEXT, +} hicn_face_ip6_input_next_t; + +#define NEXT_MAPME_IP4 HICN_FACE_IP4_INPUT_NEXT_MAPME +#define NEXT_MAPME_IP6 HICN_FACE_IP6_INPUT_NEXT_MAPME +#define NEXT_DATA_IP4 HICN_FACE_IP4_INPUT_NEXT_DATA +#define NEXT_DATA_IP6 HICN_FACE_IP6_INPUT_NEXT_DATA + +#define NEXT_ERROR_DROP_IP4 HICN_FACE_IP4_INPUT_NEXT_ERROR_DROP +#define NEXT_ERROR_DROP_IP6 HICN_FACE_IP6_INPUT_NEXT_ERROR_DROP + +#define IP_HEADER_4 ip4_header_t +#define IP_HEADER_6 ip6_header_t + +#define LOCK_FROM_LOCAL_IP4 hicn_dpo_ip4_lock_from_local +#define LOCK_FROM_LOCAL_IP6 hicn_dpo_ip6_lock_from_local + +#define TRACE_INPUT_PKT_IP4 hicn_face_ip4_input_trace_t +#define TRACE_INPUT_PKT_IP6 hicn_face_ip6_input_trace_t + +/* + * NOTE: Both hicn_face_ip4_input_node_fn and hicn_face_ip6_input_node_fn + * present a similar codebase. Macro are hard to debug, although the + * followind code is pretty straighforward and most of the complexity is in + * functions that can be easily debug. + */ +#define face_input_x1(ipv) \ + do{ \ + vlib_buffer_t *b0; \ + u32 bi0; \ + u32 next0 = NEXT_ERROR_DROP_IP##ipv; \ + IP_HEADER_##ipv * ip_hdr = NULL; \ + hicn_buffer_t * hicnb0; \ + int ret; \ + /* Prefetch for next iteration. */ \ + if (n_left_from > 1) \ + { \ + vlib_buffer_t *b1; \ + b1 = vlib_get_buffer (vm, from[1]); \ + CLIB_PREFETCH (b1, 2*CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b1->data, CLIB_CACHE_LINE_BYTES , LOAD); \ + } \ + /* Dequeue a packet buffer */ \ + bi0 = from[0]; \ + from += 1; \ + n_left_from -= 1; \ + to_next[0] = bi0; \ + to_next += 1; \ + n_left_to_next -= 1; \ + \ + b0 = vlib_get_buffer (vm, bi0); \ + hicnb0 = hicn_get_buffer(b0); \ + ip_hdr = (IP_HEADER_##ipv *) vlib_buffer_get_current(b0); \ + \ + u8 is_icmp = ip_hdr->protocol == IPPROTO_ICMPV##ipv; \ + \ + next0 = is_icmp*NEXT_MAPME_IP##ipv + \ + (1-is_icmp)*NEXT_DATA_IP##ipv; \ + \ + ret = LOCK_FROM_LOCAL_IP##ipv \ + (&(hicnb0->face_dpo_id), \ + &hicnb0->is_appface, \ + &(ip_hdr->dst_address), \ + vnet_buffer (b0)->sw_if_index[VLIB_RX]); \ + \ + if ( PREDICT_FALSE(ret != HICN_ERROR_NONE) ) \ + next0 = NEXT_ERROR_DROP_IP##ipv; \ + else \ + stats.pkts_data_count += 1; \ + \ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && \ + (b0->flags & VLIB_BUFFER_IS_TRACED))) \ + { \ + TRACE_INPUT_PKT_IP##ipv *t = \ + vlib_add_trace (vm, node, b0, sizeof (*t)); \ + t->pkt_type = HICN_PKT_TYPE_INTEREST; \ + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; \ + t->next_index = next0; \ + } \ + \ + \ + /* Verify speculative enqueue, maybe switch current next frame */ \ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, \ + to_next, n_left_to_next, \ + bi0, next0); \ + }while(0) + + +#define face_input_x2(ipv) \ + do{ \ + vlib_buffer_t *b0, *b1; \ + u32 bi0, bi1; \ + u32 next0 = NEXT_ERROR_DROP_IP##ipv; \ + u32 next1 = NEXT_ERROR_DROP_IP##ipv; \ + IP_HEADER_##ipv * ip_hdr0 = NULL; \ + IP_HEADER_##ipv * ip_hdr1 = NULL; \ + hicn_buffer_t * hicnb0; \ + hicn_buffer_t * hicnb1; \ + int ret0, ret1; \ + /* Prefetch for next iteration. */ \ + { \ + vlib_buffer_t *b2, *b3; \ + b2 = vlib_get_buffer (vm, from[2]); \ + b3 = vlib_get_buffer (vm, from[3]); \ + CLIB_PREFETCH (b2, 2*CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b3, 2*CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b2->data, CLIB_CACHE_LINE_BYTES , LOAD); \ + CLIB_PREFETCH (b3->data, CLIB_CACHE_LINE_BYTES , LOAD); \ + } \ + /* Dequeue a packet buffer */ \ + bi0 = from[0]; \ + bi1 = from[1]; \ + from += 2; \ + n_left_from -= 2; \ + to_next[0] = bi0; \ + to_next[1] = bi1; \ + to_next += 2; \ + n_left_to_next -= 2; \ + \ + b0 = vlib_get_buffer (vm, bi0); \ + b1 = vlib_get_buffer (vm, bi1); \ + hicnb0 = hicn_get_buffer(b0); \ + hicnb1 = hicn_get_buffer(b1); \ + ip_hdr0 = (IP_HEADER_##ipv *) vlib_buffer_get_current(b0); \ + ip_hdr1 = (IP_HEADER_##ipv *) vlib_buffer_get_current(b1); \ + \ + u8 is_icmp0 = ip_hdr0->protocol == IPPROTO_ICMPV##ipv; \ + u8 is_icmp1 = ip_hdr1->protocol == IPPROTO_ICMPV##ipv; \ + \ + next0 = is_icmp0*NEXT_MAPME_IP##ipv + \ + (1-is_icmp0)*NEXT_DATA_IP##ipv; \ + \ + next1 = is_icmp1*NEXT_MAPME_IP##ipv + \ + (1-is_icmp1)*NEXT_DATA_IP##ipv; \ + \ + \ + ret0 = LOCK_FROM_LOCAL_IP##ipv \ + (&(hicnb0->face_dpo_id), \ + &hicnb0->is_appface, \ + &(ip_hdr0->dst_address), \ + vnet_buffer (b0)->sw_if_index[VLIB_RX]); \ + \ + ret1 = LOCK_FROM_LOCAL_IP##ipv \ + (&(hicnb1->face_dpo_id), \ + &hicnb1->is_appface, \ + &(ip_hdr1->dst_address), \ + vnet_buffer (b1)->sw_if_index[VLIB_RX]); \ + \ + if ( PREDICT_FALSE(ret0 != HICN_ERROR_NONE) ) \ + next0 = NEXT_ERROR_DROP_IP##ipv; \ + else \ + stats.pkts_data_count += 1; \ + \ + if ( PREDICT_FALSE(ret1 != HICN_ERROR_NONE) ) \ + next1 = NEXT_ERROR_DROP_IP##ipv; \ + else \ + stats.pkts_data_count += 1; \ + \ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && \ + (b0->flags & VLIB_BUFFER_IS_TRACED))) \ + { \ + TRACE_INPUT_PKT_IP##ipv *t = \ + vlib_add_trace (vm, node, b0, sizeof (*t)); \ + t->pkt_type = HICN_PKT_TYPE_INTEREST; \ + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; \ + t->next_index = next0; \ + } \ + \ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && \ + (b1->flags & VLIB_BUFFER_IS_TRACED))) \ + { \ + TRACE_INPUT_PKT_IP##ipv *t = \ + vlib_add_trace (vm, node, b1, sizeof (*t)); \ + t->pkt_type = HICN_PKT_TYPE_INTEREST; \ + t->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX]; \ + t->next_index = next1; \ + } \ + \ + \ + /* Verify speculative enqueue, maybe switch current next frame */ \ + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, \ + to_next, n_left_to_next, \ + bi0, bi1, next0, next1); \ + }while(0) + + +static uword +hicn_face_ip4_input_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next, next_index; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + + while (n_left_from > 0) + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + /* Dual loop, X2 */ + while (n_left_from >= 4 && n_left_to_next >= 2) + { + face_input_x2 (4); + } + + /* Dual loop, X1 */ + while (n_left_from > 0 && n_left_to_next > 0) + { + face_input_x1 (4); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, node->node_index, + HICNFWD_ERROR_DATAS, stats.pkts_data_count); + + return (frame->n_vectors); +} + +/* packet trace format function */ +static u8 * +hicn_face_ip4_input_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_face_ip4_input_trace_t *t = + va_arg (*args, hicn_face_ip4_input_trace_t *); + + s = format (s, "FACE_IP4_INPUT: pkt: %d, sw_if_index %d, next index %d", + (int) t->pkt_type, t->sw_if_index, t->next_index); + return (s); +} + + +/* + * Node registration for the interest forwarder node + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE(hicn_face_ip4_input_node) = +{ + .function = hicn_face_ip4_input_node_fn, + .name = "hicn-face-ip4-input", + .vector_size = sizeof(u32), + .format_trace = hicn_face_ip4_input_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(hicn_face_ip4_input_error_strings), + .error_strings = hicn_face_ip4_input_error_strings, + .n_next_nodes = HICN_FACE_IP4_INPUT_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = + { + [HICN_FACE_IP4_INPUT_NEXT_DATA] = "hicn-data-pcslookup", + [HICN_FACE_IP4_INPUT_NEXT_MAPME] = "hicn-mapme-ack", + [HICN_FACE_IP4_INPUT_NEXT_ERROR_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +/** + * @brief IPv6 face input node function + * @see hicn_face_ip4_input_node_fn + */ +static uword +hicn_face_ip6_input_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next, next_index; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + + while (n_left_from > 0) + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + /* Dual loop, X2 */ + while (n_left_from >= 4 && n_left_to_next >= 2) + { + face_input_x2 (6); + } + + /* Dual loop, X1 */ + while (n_left_from > 0 && n_left_to_next > 0) + { + face_input_x1 (6); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, node->node_index, + HICNFWD_ERROR_DATAS, stats.pkts_data_count); + + return (frame->n_vectors); +} + +/* packet trace format function */ +static u8 * +hicn_face_ip6_input_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_face_ip6_input_trace_t *t = + va_arg (*args, hicn_face_ip6_input_trace_t *); + + s = format (s, "FACE_IP6_INPUT: pkt: %d, sw_if_index %d, next index %d", + (int) t->pkt_type, t->sw_if_index, t->next_index); + return (s); +} + +/* + * Node registration for the interest forwarder node + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE(hicn_face_ip6_input_node) = +{ + .function = hicn_face_ip6_input_node_fn, + .name = "hicn-face-ip6-input", + .vector_size = sizeof(u32), + .format_trace = hicn_face_ip6_input_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(hicn_face_ip6_input_error_strings), + .error_strings = hicn_face_ip6_input_error_strings, + .n_next_nodes = HICN_FACE_IP6_INPUT_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = + { + [HICN_FACE_IP6_INPUT_NEXT_DATA] = "hicn-data-pcslookup", + [HICN_FACE_IP6_INPUT_NEXT_MAPME] = "hicn-mapme-ack", + [HICN_FACE_IP6_INPUT_NEXT_ERROR_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +/**** FACE OUTPUT *****/ + +static inline void +hicn_face_rewrite_interest (vlib_main_t * vm, vlib_buffer_t * b0, + const hicn_face_t * face, u32 * next) +{ + ip_adjacency_t *adj = adj_get (face->shared.adj); + + /* We assume the ip adjacency has already the MAC/link layer address */ + vnet_buffer (b0)->ip.adj_index[VLIB_TX] = face->shared.adj; + hicn_header_t *hicn = vlib_buffer_get_current (b0); + + hicn_face_ip_t *ip_face = (hicn_face_ip_t *) face->data; + + ip46_address_t temp_addr; + ip46_address_reset (&temp_addr); + hicn_type_t type = hicn_get_buffer (b0)->type; + hicn_ops_vft[type.l1]->rewrite_interest (type, &hicn->protocol, + &ip_face->local_addr, &temp_addr); + + /* We rewrite the dst address to send an arp/neighbour discovert request */ + if (PREDICT_FALSE + (adj->lookup_next_index == IP_LOOKUP_NEXT_ARP + || adj->lookup_next_index == IP_LOOKUP_NEXT_GLEAN)) + hicn_ops_vft[type.l1]->rewrite_data (type, &hicn->protocol, + &ip_face->remote_addr, &temp_addr, + 0); + + *next = adj->lookup_next_index; +} + +static char *hicn_face_ip4_output_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + +static char *hicn_face_ip6_output_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; +} hicn_face_ip4_output_trace_t; + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; +} hicn_face_ip6_output_trace_t; + +#define TRACE_OUTPUT_PKT_IP4 hicn_face_ip4_output_trace_t +#define TRACE_OUTPUT_PKT_IP6 hicn_face_ip6_output_trace_t + +#define face_output_x1(ipv) \ + do { \ + vlib_buffer_t *b0; \ + u32 bi0; \ + u32 next0 = IP_LOOKUP_NEXT_DROP; \ + hicn_face_t * face; \ + \ + /* Prefetch for next iteration. */ \ + if (n_left_from > 1) \ + { \ + vlib_buffer_t *b1; \ + b1 = vlib_get_buffer (vm, from[1]); \ + CLIB_PREFETCH (b1, CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b1->data, CLIB_CACHE_LINE_BYTES , STORE); \ + } \ + /* Dequeue a packet buffer */ \ + bi0 = from[0]; \ + from += 1; \ + n_left_from -= 1; \ + to_next[0] = bi0; \ + to_next += 1; \ + n_left_to_next -= 1; \ + \ + b0 = vlib_get_buffer (vm, bi0); \ + \ + face = \ + hicn_dpoi_get_from_idx (vnet_buffer (b0)->ip.adj_index[VLIB_TX]); \ + \ + if (PREDICT_TRUE(face != NULL)) \ + { \ + hicn_face_rewrite_interest \ + (vm, b0, face, &next0); \ + stats.pkts_interest_count += 1; \ + } \ + \ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && \ + (b0->flags & VLIB_BUFFER_IS_TRACED))) \ + { \ + TRACE_OUTPUT_PKT_IP##ipv *t = \ + vlib_add_trace (vm, node, b0, sizeof (*t)); \ + t->pkt_type = HICN_PKT_TYPE_INTEREST; \ + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; \ + t->next_index = next0; \ + } \ + \ + \ + /* Verify speculative enqueue, maybe switch current next frame */ \ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, \ + to_next, n_left_to_next, \ + bi0, next0); \ + }while(0) + +#define face_output_x2(ipv) \ + do { \ + vlib_buffer_t *b0, *b1; \ + u32 bi0, bi1; \ + u32 next0 = IP_LOOKUP_NEXT_DROP; \ + u32 next1 = IP_LOOKUP_NEXT_DROP; \ + hicn_face_t *face0, *face1; \ + \ + /* Prefetch for next iteration. */ \ + { \ + vlib_buffer_t *b2, *b3; \ + b2 = vlib_get_buffer (vm, from[2]); \ + b3 = vlib_get_buffer (vm, from[3]); \ + CLIB_PREFETCH (b2, CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b3, CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b2->data, CLIB_CACHE_LINE_BYTES , STORE); \ + CLIB_PREFETCH (b3->data, CLIB_CACHE_LINE_BYTES , STORE); \ + } \ + /* Dequeue a packet buffer */ \ + bi0 = from[0]; \ + bi1 = from[1]; \ + from += 2; \ + n_left_from -= 2; \ + to_next[0] = bi0; \ + to_next[1] = bi1; \ + to_next += 2; \ + n_left_to_next -= 2; \ + \ + b0 = vlib_get_buffer (vm, bi0); \ + b1 = vlib_get_buffer (vm, bi1); \ + \ + face0 = \ + hicn_dpoi_get_from_idx (vnet_buffer (b0)->ip.adj_index[VLIB_TX]); \ + face1 = \ + hicn_dpoi_get_from_idx (vnet_buffer (b1)->ip.adj_index[VLIB_TX]); \ + \ + if (PREDICT_TRUE(face0 != NULL)) \ + { \ + hicn_face_rewrite_interest \ + (vm, b0, face0, &next0); \ + stats.pkts_interest_count += 1; \ + } \ + \ + if (PREDICT_TRUE(face1 != NULL)) \ + { \ + hicn_face_rewrite_interest \ + (vm, b1, face1, &next1); \ + stats.pkts_interest_count += 1; \ + } \ + \ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && \ + (b0->flags & VLIB_BUFFER_IS_TRACED))) \ + { \ + TRACE_OUTPUT_PKT_IP##ipv *t = \ + vlib_add_trace (vm, node, b0, sizeof (*t)); \ + t->pkt_type = HICN_PKT_TYPE_INTEREST; \ + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; \ + t->next_index = next0; \ + } \ + \ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && \ + (b1->flags & VLIB_BUFFER_IS_TRACED))) \ + { \ + TRACE_OUTPUT_PKT_IP##ipv *t = \ + vlib_add_trace (vm, node, b1, sizeof (*t)); \ + t->pkt_type = HICN_PKT_TYPE_INTEREST; \ + t->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX]; \ + t->next_index = next1; \ + } \ + \ + \ + /* Verify speculative enqueue, maybe switch current next frame */ \ + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, \ + to_next, n_left_to_next, \ + bi0, bi1, next0, next1); \ + }while(0) + + +static uword +hicn_face_ip4_output_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next, next_index; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + /* Dual loop, X2 */ + while (n_left_from >= 4 && n_left_to_next >= 2) + { + face_output_x2 (4); + } + + /* Dual loop, X1 */ + while (n_left_from > 0 && n_left_to_next > 0) + { + face_output_x1 (4); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, node->node_index, + HICNFWD_ERROR_INTERESTS, + stats.pkts_interest_count); + + return (frame->n_vectors); +} + +/* packet trace format function */ +static u8 * +hicn_face_ip4_output_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_face_ip4_output_trace_t *t = + va_arg (*args, hicn_face_ip4_output_trace_t *); + + s = format (s, "FACE_IP4_OUTPUT: pkt: %d, sw_if_index %d, next index %d", + (int) t->pkt_type, t->sw_if_index, t->next_index); + return (s); +} + +/* + * Node registration for the interest forwarder node + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE(hicn_face_ip4_output_node) = +{ + .function = hicn_face_ip4_output_node_fn, + .name = "hicn-face-ip4-output", + .vector_size = sizeof(u32), + .format_trace = hicn_face_ip4_output_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(hicn_face_ip4_output_error_strings), + .error_strings = hicn_face_ip4_output_error_strings, + .n_next_nodes = IP4_LOOKUP_N_NEXT, + /* Reusing the list of nodes from lookup to be compatible with arp */ + .next_nodes = IP4_LOOKUP_NEXT_NODES, +}; +/* *INDENT-ON* */ + + +static uword +hicn_face_ip6_output_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next, next_index; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + /* Dual loop, X2 */ + while (n_left_from >= 4 && n_left_to_next >= 2) + { + face_output_x2 (6); + } + + /* Dual loop, X1 */ + while (n_left_from > 0 && n_left_to_next > 0) + { + face_output_x1 (6); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, node->node_index, + HICNFWD_ERROR_INTERESTS, + stats.pkts_interest_count); + + return (frame->n_vectors); +} + +/* packet trace format function */ +static u8 * +hicn_face_ip6_output_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_face_ip6_output_trace_t *t = + va_arg (*args, hicn_face_ip6_output_trace_t *); + + s = format (s, "FACE_IP6_OUTPUT: pkt: %d, sw_if_index %d, next index %d", + (int) t->pkt_type, t->sw_if_index, t->next_index); + return (s); +} + +/* + * Node registration for the interest forwarder node + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE(hicn_face_ip6_output_node) = +{ + .function = hicn_face_ip6_output_node_fn, + .name = "hicn-face-ip6-output", + .vector_size = sizeof(u32), + .format_trace = hicn_face_ip6_output_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(hicn_face_ip6_output_error_strings), + .error_strings = hicn_face_ip6_output_error_strings, + .n_next_nodes = IP6_LOOKUP_N_NEXT, + /* Reusing the list of nodes from lookup to be compatible with neighbour discovery */ + .next_nodes = IP6_LOOKUP_NEXT_NODES, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/faces/ip/face_ip_node.h b/hicn-plugin/src/faces/ip/face_ip_node.h new file mode 100755 index 000000000..000395a04 --- /dev/null +++ b/hicn-plugin/src/faces/ip/face_ip_node.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_FACE_IP_NODE_H__ +#define __HICN_FACE_IP_NODE_H__ + +#include +#include + +extern vlib_node_registration_t hicn_face_ip4_input_node; +extern vlib_node_registration_t hicn_face_ip4_output_node; +extern vlib_node_registration_t hicn_face_ip6_input_node; +extern vlib_node_registration_t hicn_face_ip6_output_node; + +/** + * @brief Initialize the ip face module + */ +void hicn_face_ip_init (vlib_main_t * vm); + +#endif // __HICN_FACE_IP_NODE_H__ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/faces/ip/iface_ip_node.c b/hicn-plugin/src/faces/ip/iface_ip_node.c new file mode 100755 index 000000000..8df0467f0 --- /dev/null +++ b/hicn-plugin/src/faces/ip/iface_ip_node.c @@ -0,0 +1,845 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "face_ip.h" +#include "dpo_ip.h" +#include "../../strategy_dpo_manager.h" +#include "../face.h" +#include "../../infra.h" +#include "../../cache_policies/cs_lru.h" + +/** + * @File + * + * Definition of the nodes for ip incomplete faces. + */ + +vlib_node_registration_t hicn_iface_ip4_input_node; +vlib_node_registration_t hicn_iface_ip4_output_node; +vlib_node_registration_t hicn_iface_ip6_input_node; +vlib_node_registration_t hicn_iface_ip6_output_node; + +u32 data_fwd_iface_ip4_vlib_edge; +u32 data_fwd_iface_ip6_vlib_edge; + +void +hicn_iface_ip_init (vlib_main_t * vm) +{ + u32 temp_index4 = vlib_node_add_next (vm, + hicn_interest_hitcs_node.index, + hicn_iface_ip4_output_node.index); + u32 temp_index6 = vlib_node_add_next (vm, + hicn_interest_hitcs_node.index, + hicn_iface_ip6_output_node.index); + + data_fwd_iface_ip4_vlib_edge = vlib_node_add_next (vm, + hicn_data_fwd_node.index, + hicn_iface_ip4_output_node. + index); + + data_fwd_iface_ip6_vlib_edge = vlib_node_add_next (vm, + hicn_data_fwd_node.index, + hicn_iface_ip6_output_node. + index); + + ASSERT (temp_index4 == data_fwd_iface_ip4_vlib_edge); + ASSERT (temp_index6 == data_fwd_iface_ip6_vlib_edge); +} + +static char *hicn_iface_ip4_input_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + +static char *hicn_iface_ip6_input_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; +} hicn_iface_ip4_input_trace_t; + +typedef enum +{ + HICN_IFACE_IP4_INPUT_NEXT_INTEREST, + HICN_IFACE_IP4_INPUT_NEXT_MAPME, + HICN_IFACE_IP4_INPUT_NEXT_ERROR_DROP, + HICN_IFACE_IP4_INPUT_N_NEXT, +} hicn_iface_ip4_input_next_t; + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; +} hicn_iface_ip6_input_trace_t; + +typedef enum +{ + HICN_IFACE_IP6_INPUT_NEXT_INTEREST, + HICN_IFACE_IP6_INPUT_NEXT_MAPME, + HICN_IFACE_IP6_INPUT_NEXT_ERROR_DROP, + HICN_IFACE_IP6_INPUT_N_NEXT, +} hicn_iface_ip6_input_next_t; + +#define NEXT_MAPME_IP4 HICN_IFACE_IP4_INPUT_NEXT_MAPME +#define NEXT_MAPME_IP6 HICN_IFACE_IP6_INPUT_NEXT_MAPME + +#define NEXT_INTEREST_IP4 HICN_IFACE_IP6_INPUT_NEXT_INTEREST +#define NEXT_INTEREST_IP6 HICN_IFACE_IP6_INPUT_NEXT_INTEREST + +#define ADDRESS_IP4 ip_interface_address_t *ia = 0;ip4_address_t *local_address = ip4_interface_first_address(&ip4_main, swif, &ia) +#define ADDRESS_IP6 ip6_address_t *local_address = ip6_interface_first_address(&ip6_main, swif) + +#define ADDRESSX2_IP4 ip_interface_address_t *ia0, *ia1; ia0 = ia1 = 0; \ + ip4_address_t *local_address0 = ip4_interface_first_address(&ip4_main, swif0, &ia0); \ + ip4_address_t *local_address1 = ip4_interface_first_address(&ip4_main, swif1, &ia1); + +#define ADDRESSX2_IP6 ip6_address_t *local_address0 = ip6_interface_first_address(&ip6_main, swif0); \ + ip6_address_t *local_address1 = ip6_interface_first_address(&ip6_main, swif1); + +#define DPO_ADD_LOCK_IP4 hicn_dpo_ip4_add_and_lock_from_remote +#define DPO_ADD_LOCK_IP6 hicn_dpo_ip6_add_and_lock_from_remote + +#define VLIB_EDGE_IP4 data_fwd_iface_ip4_vlib_edge +#define VLIB_EDGE_IP6 data_fwd_iface_ip6_vlib_edge + +#define IP_HEADER_4 ip4_header_t +#define IP_HEADER_6 ip6_header_t + +#define TRACE_INPUT_PKT_IP4 hicn_iface_ip4_input_trace_t +#define TRACE_INPUT_PKT_IP6 hicn_iface_ip6_input_trace_t + +#define iface_input_x1(ipv) \ + do { \ + vlib_buffer_t *b0; \ + u32 bi0, next0; \ + IP_HEADER_##ipv * ip_hdr = NULL; \ + hicn_buffer_t * hicnb0; \ + u32 swif; \ + /* Prefetch for next iteration. */ \ + if (n_left_from > 1) \ + { \ + vlib_buffer_t *b1; \ + b1 = vlib_get_buffer (vm, from[1]); \ + CLIB_PREFETCH (b1, 2*CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b1->data, CLIB_CACHE_LINE_BYTES , LOAD); \ + } \ + /* Dequeue a packet buffer */ \ + bi0 = from[0]; \ + from += 1; \ + n_left_from -= 1; \ + to_next[0] = bi0; \ + to_next += 1; \ + n_left_to_next -= 1; \ + \ + b0 = vlib_get_buffer (vm, bi0); \ + hicnb0 = hicn_get_buffer(b0); \ + ip_hdr = (IP_HEADER_##ipv *) vlib_buffer_get_current(b0); \ + \ + stats.pkts_interest_count += 1; \ + \ + u8 is_icmp = ip_hdr->protocol == IPPROTO_ICMPV##ipv; \ + \ + next0 = is_icmp*NEXT_MAPME_IP##ipv + \ + (1-is_icmp)*NEXT_INTEREST_IP##ipv; \ + \ + swif = vnet_buffer (b0)->sw_if_index[VLIB_RX]; \ + \ + ADDRESS_IP##ipv; \ + \ + DPO_ADD_LOCK_IP##ipv \ + (&(hicnb0->face_dpo_id), \ + &hicnb0->is_appface, \ + local_address, \ + &(ip_hdr->src_address), \ + vnet_buffer(b0)->sw_if_index[VLIB_RX], \ + VLIB_EDGE_IP##ipv); \ + \ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && \ + (b0->flags & VLIB_BUFFER_IS_TRACED))) \ + { \ + TRACE_INPUT_PKT_IP##ipv *t = \ + vlib_add_trace (vm, node, b0, sizeof (*t)); \ + t->pkt_type = HICN_PKT_TYPE_INTEREST; \ + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; \ + t->next_index = next0; \ + } \ + \ + \ + /* Verify speculative enqueue, maybe switch current next frame */ \ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, \ + to_next, n_left_to_next, \ + bi0, next0); \ + }while(0) + + +#define iface_input_x2(ipv) \ + do { \ + vlib_buffer_t *b0, *b1; \ + u32 bi0, bi1, next0, next1; \ + IP_HEADER_##ipv * ip_hdr0 = NULL; \ + IP_HEADER_##ipv * ip_hdr1 = NULL; \ + hicn_buffer_t *hicnb0, *hicnb1; \ + u32 swif0, swif1; \ + \ + /* Prefetch for next iteration. */ \ + vlib_buffer_t *b2, *b3; \ + b2 = vlib_get_buffer (vm, from[2]); \ + b3 = vlib_get_buffer (vm, from[3]); \ + CLIB_PREFETCH (b2, 2*CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b3, 2*CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b2->data, CLIB_CACHE_LINE_BYTES , LOAD); \ + CLIB_PREFETCH (b3->data, CLIB_CACHE_LINE_BYTES , LOAD); \ + \ + /* Dequeue a packet buffer */ \ + bi0 = from[0]; \ + bi1 = from[1]; \ + from += 2; \ + n_left_from -= 2; \ + to_next[0] = bi0; \ + to_next[1] = bi1; \ + to_next += 2; \ + n_left_to_next -= 2; \ + \ + b0 = vlib_get_buffer (vm, bi0); \ + b1 = vlib_get_buffer (vm, bi1); \ + hicnb0 = hicn_get_buffer(b0); \ + hicnb1 = hicn_get_buffer(b1); \ + ip_hdr0 = (IP_HEADER_##ipv *) vlib_buffer_get_current(b0); \ + ip_hdr1 = (IP_HEADER_##ipv *) vlib_buffer_get_current(b1); \ + \ + stats.pkts_interest_count += 2; \ + \ + u8 is_icmp0 = ip_hdr0->protocol == IPPROTO_ICMPV##ipv; \ + u8 is_icmp1 = ip_hdr1->protocol == IPPROTO_ICMPV##ipv; \ + \ + next0 = is_icmp0*NEXT_MAPME_IP##ipv + \ + (1-is_icmp0)*NEXT_INTEREST_IP##ipv; \ + \ + next1 = is_icmp1*NEXT_MAPME_IP##ipv + \ + (1-is_icmp1)*NEXT_INTEREST_IP##ipv; \ + \ + swif0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; \ + swif1 = vnet_buffer (b1)->sw_if_index[VLIB_RX]; \ + \ + ADDRESSX2_IP##ipv; \ + \ + DPO_ADD_LOCK_IP##ipv \ + (&(hicnb0->face_dpo_id), \ + &hicnb0->is_appface, \ + local_address0, \ + &(ip_hdr0->src_address), \ + vnet_buffer(b0)->sw_if_index[VLIB_RX], \ + VLIB_EDGE_IP##ipv); \ + \ + DPO_ADD_LOCK_IP##ipv \ + (&(hicnb1->face_dpo_id), \ + &hicnb1->is_appface, \ + local_address1, \ + &(ip_hdr1->src_address), \ + vnet_buffer(b1)->sw_if_index[VLIB_RX], \ + VLIB_EDGE_IP##ipv); \ + \ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && \ + (b0->flags & VLIB_BUFFER_IS_TRACED))) \ + { \ + TRACE_INPUT_PKT_IP##ipv *t = \ + vlib_add_trace (vm, node, b0, sizeof (*t)); \ + t->pkt_type = HICN_PKT_TYPE_INTEREST; \ + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; \ + t->next_index = next0; \ + } \ + \ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && \ + (b1->flags & VLIB_BUFFER_IS_TRACED))) \ + { \ + TRACE_INPUT_PKT_IP##ipv *t = \ + vlib_add_trace (vm, node, b1, sizeof (*t)); \ + t->pkt_type = HICN_PKT_TYPE_INTEREST; \ + t->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX]; \ + t->next_index = next1; \ + } \ + \ + /* Verify speculative enqueue, maybe switch current next frame */ \ + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, \ + to_next, n_left_to_next, \ + bi0, bi1, next0, next1); \ + }while(0) + +static uword +hicn_iface_ip4_input_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next, next_index; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + + while (n_left_from > 0) + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + /* Dual loop, X2 */ + while (n_left_from >= 4 && n_left_to_next >= 2) + { + iface_input_x2 (4); + } + + /* Dual loop, X1 */ + while (n_left_from > 0 && n_left_to_next > 0) + { + iface_input_x1 (4); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, node->node_index, + HICNFWD_ERROR_INTERESTS, + stats.pkts_interest_count); + + return (frame->n_vectors); +} + +/* packet trace format function */ +static u8 * +hicn_iface_ip4_input_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_iface_ip4_input_trace_t *t = + va_arg (*args, hicn_iface_ip4_input_trace_t *); + + s = format (s, "IFACE_IP4_INPUT: pkt: %d, sw_if_index %d, next index %d", + (int) t->pkt_type, t->sw_if_index, t->next_index); + return (s); +} + +/* + * Node registration for the interest forwarder node + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (hicn_iface_ip4_input_node) = +{ + .function = hicn_iface_ip4_input_node_fn, + .name = "hicn-iface-ip4-input", + .vector_size = sizeof (u32), + .format_trace = hicn_iface_ip4_input_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (hicn_iface_ip4_input_error_strings), + .error_strings = hicn_iface_ip4_input_error_strings, + .n_next_nodes = HICN_IFACE_IP4_INPUT_N_NEXT, + /* edit / add dispositions*/ + .next_nodes = + { + [HICN_IFACE_IP4_INPUT_NEXT_INTEREST] = "hicn-interest-pcslookup", + [HICN_IFACE_IP4_INPUT_NEXT_MAPME] = "hicn-mapme-ctrl", + [HICN_IFACE_IP4_INPUT_NEXT_ERROR_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +static uword +hicn_iface_ip6_input_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next, next_index; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + + while (n_left_from > 0) + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + /* Dual loop, X2 */ + while (n_left_from >= 4 && n_left_to_next >= 2) + { + iface_input_x2 (6); + } + + /* Dual loop, X1 */ + while (n_left_from > 0 && n_left_to_next > 0) + { + iface_input_x1 (6); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, node->node_index, + HICNFWD_ERROR_INTERESTS, + stats.pkts_interest_count); + + return (frame->n_vectors); +} + +/* packet trace format function */ +static u8 * +hicn_iface_ip6_input_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_iface_ip6_input_trace_t *t = + va_arg (*args, hicn_iface_ip6_input_trace_t *); + + s = format (s, "IFACE_IP6_INPUT: pkt: %d, sw_if_index %d, next index %d", + (int) t->pkt_type, t->sw_if_index, t->next_index); + return (s); +} + +/* + * Node registration for the interest forwarder node + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (hicn_iface_ip6_input_node) = +{ + .function = hicn_iface_ip6_input_node_fn, + .name = "hicn-iface-ip6-input", + .vector_size = sizeof (u32), + .format_trace = hicn_iface_ip6_input_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (hicn_iface_ip6_input_error_strings), + .error_strings = hicn_iface_ip6_input_error_strings, + .n_next_nodes = HICN_IFACE_IP6_INPUT_N_NEXT, + /* edit / add dispositions*/ + .next_nodes = + { + [HICN_IFACE_IP6_INPUT_NEXT_INTEREST] = "hicn-interest-pcslookup", + [HICN_IFACE_IP6_INPUT_NEXT_MAPME] = "hicn-mapme-ctrl", + [HICN_IFACE_IP6_INPUT_NEXT_ERROR_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + + +/**** IFACE OUTPUT *****/ + +static inline void +hicn_rewrite_iface_data4 (vlib_main_t * vm, vlib_buffer_t * b0, + const hicn_face_t * iface) +{ + ip4_header_t *ip0; + + /* Get the pointer to the old ip and tcp header */ + ip0 = vlib_buffer_get_current (b0); + + /* Set up the ip6 header */ + /* IP4 lenght contains the size of the ip4 header too */ + u16 sval = (vlib_buffer_length_in_chain (vm, b0)); + ip0->length = clib_host_to_net_u16 (sval); + ip0->ttl = 254; // FIXME TTL + + vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ~0; + hicn_header_t *hicn = vlib_buffer_get_current (b0); + + ip46_address_t temp_addr; + ip46_address_reset (&temp_addr); + hicn_face_ip_t *iface_ip = (hicn_face_ip_t *) iface->data; + hicn_type_t type = hicn_get_buffer (b0)->type; + hicn_ops_vft[type.l1]->rewrite_data (type, &hicn->protocol, + &(iface_ip->remote_addr), &(temp_addr), + iface->shared.pl_id); +} + +static inline void +hicn_rewrite_iface_data6 (vlib_main_t * vm, vlib_buffer_t * b0, + const hicn_face_t * iface) +{ + ip6_header_t *ip0; + + /* Get the pointer to the old ip and tcp header */ + /* Copy the previous ip and tcp header to the new portion of memory */ + ip0 = vlib_buffer_get_current (b0); + + /* Set up the ip6 header */ + /* IP6 lenght does not include the size of the ip6 header */ + u16 sval = (vlib_buffer_length_in_chain (vm, b0) - (sizeof (ip6_header_t))); + ip0->payload_length = clib_host_to_net_u16 (sval); + ip0->hop_limit = HICN_IP6_HOP_LIMIT; + + vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ~0; + hicn_header_t *hicn = vlib_buffer_get_current (b0); + + ip46_address_t temp_addr; + ip46_address_reset (&temp_addr); + hicn_face_ip_t *iface_ip = (hicn_face_ip_t *) iface->data; + hicn_type_t type = hicn_get_buffer (b0)->type; + hicn_ops_vft[type.l1]->rewrite_data (type, &hicn->protocol, + &(iface_ip->remote_addr), &(temp_addr), + iface->shared.pl_id); +} + +static char *hicn_iface_ip4_output_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + +static char *hicn_iface_ip6_output_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; +} hicn_iface_ip4_output_trace_t; + +typedef enum +{ + HICN_IFACE_IP4_OUTPUT_NEXT_LOOKUP, + HICN_IFACE_IP4_OUTPUT_NEXT_ERROR_DROP, + HICN_IFACE_IP4_OUTPUT_N_NEXT, +} hicn_iface_ip4_output_next_t; + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; +} hicn_iface_ip6_output_trace_t; + +typedef enum +{ + HICN_IFACE_IP6_OUTPUT_NEXT_LOOKUP, + HICN_IFACE_IP6_OUTPUT_NEXT_ERROR_DROP, + HICN_IFACE_IP6_OUTPUT_N_NEXT, +} hicn_iface_ip6_output_next_t; + +#define ERROR_OUTPUT_IP4 HICN_IFACE_IP4_OUTPUT_NEXT_ERROR_DROP +#define ERROR_OUTPUT_IP6 HICN_IFACE_IP6_OUTPUT_NEXT_ERROR_DROP + +#define NEXT_DATA_LOOKUP_IP4 HICN_IFACE_IP4_OUTPUT_NEXT_LOOKUP +#define NEXT_DATA_LOOKUP_IP6 HICN_IFACE_IP6_OUTPUT_NEXT_LOOKUP + +#define HICN_REWRITE_DATA_IP4 hicn_rewrite_iface_data4 +#define HICN_REWRITE_DATA_IP6 hicn_rewrite_iface_data6 + +#define TRACE_OUTPUT_PKT_IP4 hicn_iface_ip4_output_trace_t +#define TRACE_OUTPUT_PKT_IP6 hicn_iface_ip6_output_trace_t + +#define iface_output_x1(ipv) \ + do { \ + vlib_buffer_t *b0; \ + u32 bi0; \ + u32 next0 = ERROR_OUTPUT_IP##ipv; \ + hicn_face_t * face; \ + \ + /* Prefetch for next iteration. */ \ + if (n_left_from > 1) \ + { \ + vlib_buffer_t *b1; \ + b1 = vlib_get_buffer (vm, from[1]); \ + CLIB_PREFETCH (b1, CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b1->data, CLIB_CACHE_LINE_BYTES , STORE); \ + } \ + /* Dequeue a packet buffer */ \ + bi0 = from[0]; \ + from += 1; \ + n_left_from -= 1; \ + to_next[0] = bi0; \ + to_next += 1; \ + n_left_to_next -= 1; \ + \ + b0 = vlib_get_buffer (vm, bi0); \ + \ + face = \ + hicn_dpoi_get_from_idx (vnet_buffer (b0)->ip.adj_index[VLIB_TX]); \ + \ + if (PREDICT_TRUE(face != NULL)) \ + { \ + HICN_REWRITE_DATA_IP##ipv \ + (vm, b0, face); \ + next0 = NEXT_DATA_LOOKUP_IP##ipv; \ + stats.pkts_data_count += 1; \ + } \ + \ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && \ + (b0->flags & VLIB_BUFFER_IS_TRACED))) \ + { \ + TRACE_OUTPUT_PKT_IP##ipv *t = \ + vlib_add_trace (vm, node, b0, sizeof (*t)); \ + t->pkt_type = HICN_PKT_TYPE_INTEREST; \ + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; \ + t->next_index = next0; \ + } \ + \ + \ + /* Verify speculative enqueue, maybe switch current next frame */ \ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, \ + to_next, n_left_to_next, \ + bi0, next0); \ + }while(0); \ + + +#define iface_output_x2(ipv) \ + do { \ + vlib_buffer_t *b0, *b1; \ + u32 bi0, bi1; \ + u32 next0 = ERROR_OUTPUT_IP##ipv; \ + u32 next1 = ERROR_OUTPUT_IP##ipv; \ + hicn_face_t *face0, *face1; \ + \ + /* Prefetch for next iteration. */ \ + { \ + vlib_buffer_t *b2, *b3; \ + b2 = vlib_get_buffer (vm, from[2]); \ + b3 = vlib_get_buffer (vm, from[3]); \ + CLIB_PREFETCH (b2, CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b3, CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b2->data, CLIB_CACHE_LINE_BYTES , STORE); \ + CLIB_PREFETCH (b3->data, CLIB_CACHE_LINE_BYTES , STORE); \ + } \ + \ + /* Dequeue a packet buffer */ \ + bi0 = from[0]; \ + bi1 = from[1]; \ + from += 2; \ + n_left_from -= 2; \ + to_next[0] = bi0; \ + to_next[1] = bi1; \ + to_next += 2; \ + n_left_to_next -= 2; \ + \ + b0 = vlib_get_buffer (vm, bi0); \ + b1 = vlib_get_buffer (vm, bi1); \ + \ + face0 = \ + hicn_dpoi_get_from_idx (vnet_buffer (b0)->ip.adj_index[VLIB_TX]); \ + face1 = \ + hicn_dpoi_get_from_idx (vnet_buffer (b1)->ip.adj_index[VLIB_TX]); \ + \ + if (PREDICT_TRUE(face0 != NULL)) \ + { \ + HICN_REWRITE_DATA_IP##ipv \ + (vm, b0, face0); \ + next0 = NEXT_DATA_LOOKUP_IP##ipv; \ + stats.pkts_data_count += 1; \ + } \ + \ + if (PREDICT_TRUE(face1 != NULL)) \ + { \ + HICN_REWRITE_DATA_IP##ipv \ + (vm, b1, face1); \ + next1 = NEXT_DATA_LOOKUP_IP##ipv; \ + stats.pkts_data_count += 1; \ + } \ + \ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && \ + (b0->flags & VLIB_BUFFER_IS_TRACED))) \ + { \ + TRACE_OUTPUT_PKT_IP##ipv *t = \ + vlib_add_trace (vm, node, b0, sizeof (*t)); \ + t->pkt_type = HICN_PKT_TYPE_INTEREST; \ + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; \ + t->next_index = next0; \ + } \ + \ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && \ + (b1->flags & VLIB_BUFFER_IS_TRACED))) \ + { \ + TRACE_OUTPUT_PKT_IP##ipv *t = \ + vlib_add_trace (vm, node, b1, sizeof (*t)); \ + t->pkt_type = HICN_PKT_TYPE_INTEREST; \ + t->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX]; \ + t->next_index = next1; \ + } \ + \ + \ + /* Verify speculative enqueue, maybe switch current next frame */ \ + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, \ + to_next, n_left_to_next, \ + bi0, bi1, next0, next1); \ + }while(0); \ + + + +static uword +hicn_iface_ip4_output_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next, next_index; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from >= 4 && n_left_to_next >= 2) + { + iface_output_x2 (4); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + iface_output_x1 (4); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, node->node_index, + HICNFWD_ERROR_DATAS, stats.pkts_data_count); + + return (frame->n_vectors); +} + +/* packet trace format function */ +static u8 * +hicn_iface_ip4_output_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_iface_ip4_output_trace_t *t = + va_arg (*args, hicn_iface_ip4_output_trace_t *); + + s = format (s, "IFACE_IP4_OUTPUT: pkt: %d, sw_if_index %d, next index %d", + (int) t->pkt_type, t->sw_if_index, t->next_index); + return (s); +} + +/* + * Node registration for the interest forwarder node + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (hicn_iface_ip4_output_node) = +{ + .function = hicn_iface_ip4_output_node_fn, + .name = "hicn-iface-ip4-output", + .vector_size = sizeof (u32), + .format_trace = hicn_iface_ip4_output_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (hicn_iface_ip4_output_error_strings), + .error_strings = hicn_iface_ip4_output_error_strings, + .n_next_nodes = HICN_IFACE_IP4_OUTPUT_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = + { + [HICN_IFACE_IP4_OUTPUT_NEXT_LOOKUP] = "ip4-lookup", + [HICN_IFACE_IP4_OUTPUT_NEXT_ERROR_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + + +static uword +hicn_iface_ip6_output_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next, next_index; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from >= 4 && n_left_to_next >= 2) + { + iface_output_x2 (6); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + iface_output_x1 (6); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, node->node_index, + HICNFWD_ERROR_DATAS, stats.pkts_data_count); + + return (frame->n_vectors); +} + +/* packet trace format function */ +static u8 * +hicn_iface_ip6_output_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_iface_ip6_output_trace_t *t = + va_arg (*args, hicn_iface_ip6_output_trace_t *); + + s = format (s, "IFACE_IP6_OUTPUT: pkt: %d, sw_if_index %d, next index %d", + (int) t->pkt_type, t->sw_if_index, t->next_index); + return (s); +} + +/* + * Node registration for the interest forwarder node + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (hicn_iface_ip6_output_node) = +{ + .function = hicn_iface_ip6_output_node_fn, + .name = "hicn-iface-ip6-output", + .vector_size = sizeof (u32), + .format_trace = hicn_iface_ip6_output_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (hicn_iface_ip6_output_error_strings), + .error_strings = hicn_iface_ip6_output_error_strings, + .n_next_nodes = HICN_IFACE_IP6_OUTPUT_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = + { + [HICN_IFACE_IP6_OUTPUT_NEXT_LOOKUP] = "ip6-lookup", + [HICN_IFACE_IP6_OUTPUT_NEXT_ERROR_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/faces/ip/iface_ip_node.h b/hicn-plugin/src/faces/ip/iface_ip_node.h new file mode 100755 index 000000000..36923f069 --- /dev/null +++ b/hicn-plugin/src/faces/ip/iface_ip_node.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_IFACE_IP_NODE_H__ +#define __HICN_IFACE_IP_NODE_H__ + +#include +#include + +/** + * @brief Initialize the ip iface module + */ +void hicn_iface_ip_init (vlib_main_t * vm); + +#endif // __HICN_IFACE_IP_NODE_H__ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/faces/udp/dpo_udp.c b/hicn-plugin/src/faces/udp/dpo_udp.c new file mode 100755 index 000000000..e58fc9788 --- /dev/null +++ b/hicn-plugin/src/faces/udp/dpo_udp.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "dpo_udp.h" + +#include +#include +#include +#include + +const static char *const hicn_face_ip4udp_nodes[] = { + "hicn-face-encap-udp4", + "hicn-face-decap-udp4", + "hicn-iface-decap-udp4", + "hicn-iface-encap-udp4", + NULL, +}; + +const static char *const hicn_face_ip6udp_nodes[] = { + "hicn-face-encap-udp6", + "hicn-face-decap-udp6", + "hicn-iface-decap-udp6", + "hicn-iface-encap-udp6", + NULL, +}; + +const static char *const *const hicn_ipudp_nodes[DPO_PROTO_NUM] = { + [DPO_PROTO_IP4] = hicn_face_ip4udp_nodes, + [DPO_PROTO_IP6] = hicn_face_ip6udp_nodes +}; + + +const static dpo_vft_t hicn_dpoi_udp_vft = { + .dv_lock = hicn_face_lock, + .dv_unlock = hicn_face_unlock, + .dv_format = format_hicn_face_udp, +}; + +/* Must be executed after all the strategy nodes are created */ +void +hicn_dpo_udp_module_init (void) +{ + mhash_init (&hicn_face_udp_hashtb, sizeof (hicn_face_id_t) /* value */ , + sizeof (hicn_face_udp_key_t) /* key */ ); + + /* + * How much useful is the following registration? + * So far it seems that we need it only for setting the dpo_type. + */ + hicn_face_udp_type = + dpo_register_new_type (&hicn_dpoi_udp_vft, hicn_ipudp_nodes); +} + + +/* Here udp ports are in host order, move them to network order to do the lookup */ +int +hicn_dpo_udp4_create (dpo_id_t * dpo, + const ip4_address_t * src_ip, + const ip4_address_t * dst_ip, + u16 src_port, u16 dst_port, + u32 sw_if, + adj_index_t ip_adj, + u32 node_index, + hicn_face_flags_t flags, hicn_face_id_t * face_id) +{ + u16 net_src_port = clib_host_to_net_u16 (src_port); + u16 net_dst_port = clib_host_to_net_u16 (dst_port); + hicn_face_t *face = hicn_face_udp4_get (src_ip, dst_ip, src_port, dst_port); + + u8 is_appface; + /* ip_csum_t sum0; */ + + if (face != NULL) + return HICN_ERROR_FACE_ALREADY_CREATED; + + hicn_dpo_udp4_add_and_lock (dpo, src_ip, dst_ip, net_src_port, net_dst_port, + node_index, &is_appface); + + face = hicn_dpoi_get_from_idx (dpo->dpoi_index); + + hicn_face_udp_t *udp_face = (hicn_face_udp_t *) face->data; + + udp_face->hdrs.ip4.ip.checksum = + ip4_header_checksum (&(udp_face->hdrs.ip4.ip)); + + face->shared.flags = flags; + face->shared.adj = ip_adj; + face->shared.sw_if = sw_if; + *face_id = hicn_dpoi_get_index (face); + + return HICN_ERROR_NONE; +} + + +int +hicn_dpo_udp6_create (dpo_id_t * dpo, + const ip6_address_t * src_ip, + const ip6_address_t * dst_ip, + u16 src_port, u16 dst_port, + u32 sw_if, + adj_index_t ip_adj, + u32 node_index, + hicn_face_flags_t flags, hicn_face_id_t * face_id) +{ + u16 net_src_port = clib_host_to_net_u16 (src_port); + u16 net_dst_port = clib_host_to_net_u16 (dst_port); + hicn_face_t *face = + hicn_face_udp6_get (src_ip, dst_ip, net_src_port, net_dst_port); + u8 is_appface; + + if (face != NULL) + return HICN_ERROR_FACE_ALREADY_CREATED; + + hicn_dpo_udp6_add_and_lock (dpo, src_ip, dst_ip, net_src_port, net_dst_port, + node_index, &is_appface); + + face = hicn_dpoi_get_from_idx (dpo->dpoi_index); + + face->shared.flags = flags; + face->shared.adj = ip_adj; + face->shared.sw_if = sw_if; + *face_id = hicn_dpoi_get_index (face); + + return HICN_ERROR_NONE; +} + +void +hicn_dpo_udp_create_from_face (hicn_face_t * face, dpo_id_t * dpo, + u16 dpoi_next_node) +{ + hicn_face_id_t face_dpoi_id = hicn_dpoi_get_index (face); + hicn_face_udp_t *face_udp = (hicn_face_udp_t *) face->data; + u8 version = + (face_udp->hdrs.ip4.ip.ip_version_and_header_length & 0xf0) >> 4; + dpo_set (dpo, face->shared.face_type, + version == 4 ? DPO_PROTO_IP4 : DPO_PROTO_IP6, face_dpoi_id); + dpo->dpoi_next_node = dpoi_next_node; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/faces/udp/dpo_udp.h b/hicn-plugin/src/faces/udp/dpo_udp.h new file mode 100755 index 000000000..fdde4192b --- /dev/null +++ b/hicn-plugin/src/faces/udp/dpo_udp.h @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_DPO_UDP_H__ +#define __HICN_DPO_UDP_H__ + +#include +#include +#include + +#include "face_udp.h" +#include "../face.h" +#include "../../error.h" + + +/** + * @brief Initialize the internal structures of the dpo udp face module. + */ +void hicn_dpo_udp_module_init (void); + +/** + * @brief Create a udp face and its corresponding dpo. Meant to be used for the + * control plane. + * + * @param dpo: Data plane object that point to the face created. + * @param local_addr: Local address of the UDP tunnel + * @param remote_addr: Remote address of the UDP tunnel + * @param local_port: Local port of the UDP tunnel + * @param remote_port: Remote port of the UDP tunnel + * @param adj: Ip adjacency corresponding to the remote address in the face + * @param node_index: vlib edge index to use in the packet processing + * @param flags: Flags of the face + * @param face_id: Identifier for the face (dpoi_index) + * @return HICN_ERROR_FACE_ALREADY_CREATED if the face exists, otherwise HICN_ERROR_NONE + */ +int +hicn_dpo_udp4_create (dpo_id_t * dpo, + const ip4_address_t * local_addr, + const ip4_address_t * remote_addr, + u16 local_port, u16 remote_port, + u32 sw_if, + adj_index_t adj, + u32 node_index, + hicn_face_flags_t flags, hicn_face_id_t * face_id); + +/** + * @brief Retrieve a face using the face identifier, i.e., the quadruplet (local_addr, remote_addr, + * local_port, remote_port). This method adds a lock on the face state. + * + * @param dpo: Result of the lookup. If the face doesn't exist dpo = NULL + * @param local_addr: Local address of the UDP tunnel + * @param remote_addr: Remote address of the UDP tunnel + * @param local_port: Local port of the UDP tunnel + * @param remote_port: Remote port of the UDP tunnel + * @param is_appface: Boolean that indicates whether the face is an application + * face or not. (Currently only IP faces can be appface) + * + * @result HICN_ERROR_FACE_NOT_FOUND if the face does not exist, otherwise HICN_ERROR_NONE. + */ +always_inline int +hicn_dpo_udp4_lock (dpo_id_t * dpo, + const ip4_address_t * local_addr, + const ip4_address_t * remote_addr, + u16 local_port, u16 remote_port, u8 * is_appface) +{ + hicn_face_t *face = + hicn_face_udp4_get (local_addr, remote_addr, local_port, remote_port); + + if (PREDICT_FALSE (face == NULL)) + return HICN_ERROR_FACE_NOT_FOUND; + + index_t dpoi_index = hicn_dpoi_get_index (face); + dpo_set (dpo, hicn_face_udp_type, DPO_PROTO_IP4, dpoi_index); + dpo->dpoi_next_node = ~0; + dpo_lock (dpo); + + *is_appface = 0; + + return HICN_ERROR_NONE; +} + +/** + * @brief Retrieve, or create if it doesn't exist, a face from the face + * identifier (local_addr, remote_addr, local_port, remote_port) and returns its + * dpo. This method adds a lock on the face state. + * + * @param dpo: Result of the lookup + * @param local_addr: Local address of the UDP tunnel + * @param remote_addr: Remote address of the UDP tunnel + * @param local_port: Local port of the UDP tunnel + * @param remote_port: Remote port of the UDP tunnel + * @param is_appface: Boolean that indicates whether the face is an application + * face or not. (Currently only IP faces can be appface) + * @param node_index: vlib edge index to use in the packet processing + */ +always_inline void +hicn_dpo_udp4_add_and_lock (dpo_id_t * dpo, + const ip4_address_t * local_addr, + const ip4_address_t * remote_addr, + u16 local_port, u16 remote_port, + u32 node_index, u8 * is_appface) +{ + hicn_face_t *face = + hicn_face_udp4_get (local_addr, remote_addr, local_port, remote_port); + + if (face == NULL) + { + pool_get (hicn_dpoi_face_pool, face); + + hicn_face_udp_t *udp_face = (hicn_face_udp_t *) face->data; + + clib_memcpy (&(udp_face->hdrs.ip4.ip), &ip4_header_skl, + sizeof (ip4_header_t)); + clib_memcpy (&(udp_face->hdrs.ip4.ip.src_address), local_addr, + sizeof (ip4_address_t)); + clib_memcpy (&(udp_face->hdrs.ip4.ip.dst_address), remote_addr, + sizeof (ip4_address_t)); + + udp_face->hdrs.ip4.udp.src_port = local_port; + udp_face->hdrs.ip4.udp.dst_port = remote_port; + + face->shared.adj = ADJ_INDEX_INVALID; + face->shared.pl_id = (u16) 0; + face->shared.face_type = hicn_face_udp_type; + face->shared.flags = HICN_FACE_FLAGS_IFACE; + face->shared.locks = 0; + + hicn_face_udp_key_t key; + hicn_face_udp4_get_key (local_addr, remote_addr, local_port, + remote_port, &key); + hicn_face_id_t dpoi_index = hicn_dpoi_get_index (face); + + mhash_set_mem (&hicn_face_udp_hashtb, &key, (uword *) & dpoi_index, 0); + face = face; + + *is_appface = 0; + dpo_set (dpo, hicn_face_udp_type, DPO_PROTO_IP4, dpoi_index); + dpo->dpoi_next_node = node_index; + dpo_lock (dpo); + + return; + } + + *is_appface = 0; + + hicn_face_id_t dpoi_index = hicn_dpoi_get_index (face); + dpo_set (dpo, hicn_face_udp_type, DPO_PROTO_IP4, dpoi_index); + dpo->dpoi_next_node = node_index; + dpo_lock (dpo); +} + +/** + * @brief Create a udp face and its corresponding dpo. Meant to be used for the + * control plane. + * + * @param dpo: Data plane object that point to the face created. + * @param local_addr: Local address of the UDP tunnel + * @param remote_addr: Remote address of the UDP tunnel + * @param local_port: Local port of the UDP tunnel + * @param remote_port: Remote port of the UDP tunnel + * @param adj: Ip adjacency corresponding to the remote address in the face + * @param node_index: vlib edge index to use in the packet processing + * @param flags: Flags of the face + * @param face_id: Identifier for the face (dpoi_index) + * @return HICN_ERROR_FACE_ALREADY_CREATED if the face exists, otherwise HICN_ERROR_NONE + */ +int +hicn_dpo_udp6_create (dpo_id_t * dpo, + const ip6_address_t * local_addr, + const ip6_address_t * remote_addr, + u16 local_port, u16 remote_port, + u32 sw_if, + adj_index_t adj, + u32 node_index, + hicn_face_flags_t flags, hicn_face_id_t * face_id); + + +/** + * @brief Retrieve a face using the face identifier, i.e., the quadruplet (local_addr, remote_addr, + * local_port, remote_port). This method adds a lock on the face state. + * + * @param dpo: Result of the lookup. If the face doesn't exist dpo = NULL + * @param local_addr: Local address of the UDP tunnel + * @param remote_addr: Remote address of the UDP tunnel + * @param local_port: Local port of the UDP tunnel + * @param remote_port: Remote port of the UDP tunnel + * @param is_appface: Boolean that indicates whether the face is an application + * face or not. (Currently only IP faces can be appface) + * + * @result HICN_ERROR_FACE_NOT_FOUND if the face does not exist, otherwise HICN_ERROR_NONE. + */ +always_inline int +hicn_dpo_udp6_lock (dpo_id_t * dpo, + const ip6_address_t * local_addr, + const ip6_address_t * remote_addr, + u16 local_port, u16 remote_port, u8 * is_appface) +{ + hicn_face_t *face = + hicn_face_udp6_get (local_addr, remote_addr, local_port, remote_port); + + + if (PREDICT_FALSE (face == NULL)) + return HICN_ERROR_FACE_NOT_FOUND; + + hicn_face_id_t dpoi_index = hicn_dpoi_get_index (face); + dpo_set (dpo, hicn_face_udp_type, DPO_PROTO_IP4, dpoi_index); + dpo->dpoi_next_node = ~0; + dpo_lock (dpo); + *is_appface = 0; + + return HICN_ERROR_NONE; +} + +/** + * @brief Retrieve, or create if it doesn't exist, a face from the face + * identifier (local_addr, remote_addr, local_port, remote_port) and returns its + * dpo. This method adds a lock on the face state. + * + * @param dpo: Result of the lookup + * @param local_addr: Local address of the UDP tunnel + * @param remote_addr: Remote address of the UDP tunnel + * @param local_port: Local port of the UDP tunnel + * @param remote_port: Remote port of the UDP tunnel + * @param is_appface: Boolean that indicates whether the face is an application + * face or not. (Currently only IP faces can be appface) + * @param node_index: vlib edge index to use in the packet processing + */ +always_inline void +hicn_dpo_udp6_add_and_lock (dpo_id_t * dpo, + const ip6_address_t * local_addr, + const ip6_address_t * remote_addr, + u16 local_port, u16 remote_port, + u32 node_index, u8 * is_appface) +{ + hicn_face_t *face = + hicn_face_udp6_get (local_addr, remote_addr, local_port, remote_port); + + if (face == NULL) + { + pool_get (hicn_dpoi_face_pool, face); + + hicn_face_udp_t *udp_face = (hicn_face_udp_t *) face->data; + + clib_memcpy (&(udp_face->hdrs.ip6.ip), &ip6_header_skl, + sizeof (ip6_header_t)); + clib_memcpy (&(udp_face->hdrs.ip6.ip.src_address), local_addr, + sizeof (ip6_address_t)); + clib_memcpy (&(udp_face->hdrs.ip6.ip.dst_address), remote_addr, + sizeof (ip6_address_t)); + + udp_face->hdrs.ip6.udp.src_port = local_port; + udp_face->hdrs.ip6.udp.dst_port = remote_port; + + face->shared.adj = ADJ_INDEX_INVALID; + face->shared.pl_id = (u16) 0; + face->shared.face_type = hicn_face_udp_type; + face->shared.flags = HICN_FACE_FLAGS_IFACE; + face->shared.locks = 0; + + hicn_face_udp_key_t key; + hicn_face_udp6_get_key (local_addr, remote_addr, local_port, + remote_port, &key); + hicn_face_id_t dpoi_index = hicn_dpoi_get_index (face); + + mhash_set_mem (&hicn_face_udp_hashtb, &key, (uword *) & dpoi_index, 0); + + *is_appface = 0; + dpo_set (dpo, hicn_face_udp_type, DPO_PROTO_IP6, dpoi_index); + dpo->dpoi_next_node = node_index; + dpo_lock (dpo); + + return; + } + + *is_appface = 0; + + hicn_face_id_t dpoi_index = hicn_dpoi_get_index (face); + dpo_set (dpo, hicn_face_udp_type, DPO_PROTO_IP6, dpoi_index); + dpo->dpoi_next_node = node_index; + dpo_lock (dpo); +} + +/** + * @brief Create a dpo from a udp face + * + * @param face Face from which to create the dpo + * @return the dpo + */ +void hicn_dpo_udp_create_from_face (hicn_face_t * face, dpo_id_t * dpo, + u16 dpoi_next_node); + +#endif // __HICN_DPO_UDP_H__ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/faces/udp/face_udp.c b/hicn-plugin/src/faces/udp/face_udp.c new file mode 100755 index 000000000..92335273a --- /dev/null +++ b/hicn-plugin/src/faces/udp/face_udp.c @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "face_udp.h" +#include "face_udp_node.h" +#include "dpo_udp.h" +#include "../face.h" +#include "../../strategy.h" +#include "../../strategy_dpo_manager.h" +#include "../../hicn.h" + +#include "../../mapme.h" // HICN_MAPME_EVENT_* +#include "../../mapme_eventmgr.h" // hicn_mapme_eventmgr_process_node +extern vlib_node_registration_t hicn_mapme_eventmgr_process_node; + +mhash_t hicn_face_udp_hashtb; + +dpo_type_t hicn_face_udp_type; + +ip4_header_t ip4_header_skl = { + .ip_version_and_header_length = 0x45, + .tos = 0x00, + .length = (u16) 0, + .fragment_id = (u16) 0, + .flags_and_fragment_offset = (u16) 0, + .ttl = 254, + .protocol = IP_PROTOCOL_UDP, + .checksum = 0, + .src_address = {{0}}, + .dst_address = {{0}}, +}; + +ip6_header_t ip6_header_skl = { +#if CLIB_ARCH_IS_BIG_ENDIAN + .ip_version_traffic_class_and_flow_label = 0x60000000, +#else + .ip_version_traffic_class_and_flow_label = 0x00000060, +#endif + .payload_length = (u16) 0, + .protocol = IP_PROTOCOL_UDP, + .hop_limit = 254, + .src_address = {{0}}, + .dst_address = {{0}} +}; + +u32 strategy_face_udp4_vlib_edge; +u32 strategy_face_udp6_vlib_edge; + +/* Separated from the hicn_face_udp_init because it cannot be called by the + init macro due to dependencies with other modules not yet initialied */ +void +hicn_face_udp_init_internal () +{ + ip4_header_t *ip4_hdr = &ip4_header_skl; + ip4_header_skl.checksum = ip4_header_checksum (ip4_hdr); +} + +void +hicn_face_udp_init (vlib_main_t * vm) +{ + int strategy_nodes_n = hicn_strategy_get_all_available (); + + /* Default Strategy has index 0 and it always exists */ + strategy_face_udp4_vlib_edge = vlib_node_add_next (vm, + hicn_dpo_get_strategy_vft + (default_dpo. + hicn_dpo_get_type ())-> + get_strategy_node_index + (), + hicn_face_udp4_output_node. + index); + strategy_face_udp6_vlib_edge = + vlib_node_add_next (vm, + hicn_dpo_get_strategy_vft (default_dpo. + hicn_dpo_get_type ())-> + get_strategy_node_index (), + hicn_face_udp6_output_node.index); + + /* + * Create and edge between al the other strategy nodes + * and the udp_output nodes. + */ + for (int i = 1; i < strategy_nodes_n; i++) + { + u32 temp_index4 = vlib_node_add_next (vm, + hicn_dpo_get_strategy_vft_from_id + (i)->get_strategy_node_index (), + hicn_face_udp4_output_node.index); + u32 temp_index6 = vlib_node_add_next (vm, + hicn_dpo_get_strategy_vft_from_id + (i)->get_strategy_node_index (), + hicn_face_udp6_output_node.index); + ASSERT (temp_index4 == strategy_face_udp4_vlib_edge); + ASSERT (temp_index6 == strategy_face_udp6_vlib_edge); + } + + hicn_dpo_udp_module_init (); + + register_face_type (hicn_face_udp_type, &udp_vft, "udp");; +} + +int +hicn_face_udp_add (const ip46_address_t * local_addr, + const ip46_address_t * remote_addr, u16 local_port, + u16 remote_port, u32 swif, hicn_face_id_t * pfaceid) +{ + fib_protocol_t fib_type; + vnet_link_t link_type; + adj_index_t ip_adj; + int ret = HICN_ERROR_NONE; + dpo_proto_t dpo_proto; + + hicn_face_flags_t flags = (hicn_face_flags_t) 0; + flags |= HICN_FACE_FLAGS_FACE; + + + if (ip46_address_is_ip4 (local_addr) && ip46_address_is_ip4 (remote_addr)) + { + link_type = VNET_LINK_IP4; + fib_type = FIB_PROTOCOL_IP4; + ip_adj = adj_nbr_add_or_lock (fib_type, link_type, remote_addr, swif); + + hicn_face_t *face = + hicn_face_udp4_get (&local_addr->ip4, &remote_addr->ip4, local_port, + remote_port); + + if (face != NULL) + return HICN_ERROR_FACE_ALREADY_CREATED; + + pool_get (hicn_dpoi_face_pool, face); + + hicn_face_udp_t *udp_face = (hicn_face_udp_t *) face->data; + + clib_memcpy (&(udp_face->hdrs.ip4.ip), &ip4_header_skl, + sizeof (ip4_header_t)); + clib_memcpy (&(udp_face->hdrs.ip4.ip.src_address), &(local_addr->ip4), + sizeof (ip4_address_t)); + clib_memcpy (&(udp_face->hdrs.ip4.ip.dst_address), &(remote_addr->ip4), + sizeof (ip4_address_t)); + + udp_face->hdrs.ip4.udp.src_port = local_port; + udp_face->hdrs.ip4.udp.dst_port = remote_port; + + ip_csum_t csum = udp_face->hdrs.ip4.ip.checksum; + csum = ip_csum_sub_even (csum, ip4_header_skl.src_address.as_u32); + csum = ip_csum_sub_even (csum, ip4_header_skl.dst_address.as_u32); + csum = + ip_csum_add_even (csum, udp_face->hdrs.ip4.ip.src_address.as_u32); + csum = + ip_csum_add_even (csum, udp_face->hdrs.ip4.ip.dst_address.as_u32); + udp_face->hdrs.ip4.ip.checksum = ip_csum_fold (csum); + + face->shared.adj = ip_adj; + face->shared.sw_if = swif; + face->shared.pl_id = (u16) 0; + face->shared.face_type = hicn_face_udp_type; + face->shared.flags = flags; + face->shared.locks = 0; + + hicn_face_udp_key_t key; + hicn_face_udp4_get_key (&local_addr->ip4, &remote_addr->ip4, local_port, + remote_port, &key); + hicn_face_id_t dpoi_index = hicn_dpoi_get_index (face); + + mhash_set_mem (&hicn_face_udp_hashtb, &key, (uword *) & dpoi_index, 0); + + *pfaceid = hicn_dpoi_get_index (face); + dpo_proto = DPO_PROTO_IP4; + } + else if (!ip46_address_is_ip4 (local_addr) + && !ip46_address_is_ip4 (remote_addr)) + { + link_type = VNET_LINK_IP6; + fib_type = FIB_PROTOCOL_IP6; + ip_adj = adj_nbr_add_or_lock (fib_type, link_type, remote_addr, swif); + + hicn_face_t *face = + hicn_face_udp6_get (&local_addr->ip6, &remote_addr->ip6, local_port, + remote_port); + + if (face != NULL) + return HICN_ERROR_FACE_ALREADY_CREATED; + + pool_get (hicn_dpoi_face_pool, face); + + hicn_face_udp_t *udp_face = (hicn_face_udp_t *) face->data; + + clib_memcpy (&(udp_face->hdrs.ip6.ip), &ip6_header_skl, + sizeof (ip6_header_t)); + clib_memcpy (&(udp_face->hdrs.ip6.ip.src_address), local_addr, + sizeof (ip6_address_t)); + clib_memcpy (&(udp_face->hdrs.ip6.ip.dst_address), remote_addr, + sizeof (ip6_address_t)); + + udp_face->hdrs.ip6.udp.src_port = local_port; + udp_face->hdrs.ip6.udp.dst_port = remote_port; + + face->shared.adj = ip_adj; + face->shared.sw_if = swif; + face->shared.pl_id = (u16) 0; + face->shared.face_type = hicn_face_udp_type; + face->shared.flags = flags; + face->shared.locks = 0; + + hicn_face_udp_key_t key; + hicn_face_udp6_get_key (&local_addr->ip6, &remote_addr->ip6, local_port, + remote_port, &key); + hicn_face_id_t dpoi_index = hicn_dpoi_get_index (face); + + mhash_set_mem (&hicn_face_udp_hashtb, &key, (uword *) & dpoi_index, 0); + + *pfaceid = hicn_dpoi_get_index (face); + dpo_proto = DPO_PROTO_IP6; + } + else + { + return HICN_ERROR_IPS_ADDR_TYPE_NONUNIFORM; + } + + retx_t *retx = vlib_process_signal_event_data (vlib_get_main (), + hicn_mapme_eventmgr_process_node. + index, + HICN_MAPME_EVENT_FACE_ADD, 1, + sizeof (retx_t)); + *retx = (retx_t) + { + .prefix = 0,.dpo = (dpo_id_t) + { + .dpoi_type = hicn_face_udp_type,.dpoi_proto = + dpo_proto,.dpoi_next_node = 0,.dpoi_index = *pfaceid,} + }; + + return ret; +} + +int +hicn_face_udp_del (u32 faceid) +{ + return hicn_face_del (faceid); +} + +u8 * +format_hicn_face_udp (u8 * s, va_list * args) +{ + hicn_face_id_t face_id = va_arg (*args, index_t); + CLIB_UNUSED (u32 indent) = va_arg (*args, u32); + hicn_face_t *face; + hicn_face_udp_t *udp_face; + ip_adjacency_t *adj; + u8 ipv = 0x40; + vnet_main_t *vnm = vnet_get_main (); + + + face = hicn_dpoi_get_from_idx (face_id); + udp_face = (hicn_face_udp_t *) (face->data); + + if (face->shared.flags & HICN_FACE_FLAGS_FACE) + { + ASSERT (face->shared.adj != (adj_index_t) ~ 0); + adj = adj_get (face->shared.adj); + + s = format (s, "%U Face %d: ", format_white_space, indent, face_id); + if (udp_face->hdrs.ip4.ip.ip_version_and_header_length == ipv) + { + s = format (s, "type UDP local %U|%u ", + format_ip4_address, &udp_face->hdrs.ip4.ip.src_address, + clib_net_to_host_u16 (udp_face->hdrs.ip4.udp.src_port)); + s = + format (s, "remote %U|%u ", format_ip4_address, + &udp_face->hdrs.ip4.ip.dst_address, + clib_net_to_host_u16 (udp_face->hdrs.ip4.udp.dst_port)); + s = format (s, "%U", format_vnet_link, adj->ia_link); + s = format (s, " dev %U", format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, face->shared.sw_if)); + if ((face->shared.flags & HICN_FACE_FLAGS_DELETED)) + s = format (s, " (deleted)"); + } + else + { + s = format (s, "type UDP local %U|%u ", + format_ip6_address, &udp_face->hdrs.ip6.ip.src_address, + clib_net_to_host_u16 (udp_face->hdrs.ip6.udp.src_port)); + s = + format (s, "remote %U|%u", format_ip6_address, + &udp_face->hdrs.ip6.ip.dst_address, + clib_net_to_host_u16 (udp_face->hdrs.ip6.udp.dst_port)); + s = format (s, "%U", format_vnet_link, adj->ia_link); + s = format (s, " dev %U", format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, face->shared.sw_if)); + if ((face->shared.flags & HICN_FACE_FLAGS_DELETED)) + s = format (s, " (deleted)"); + } + } + else + { + s = format (s, "IFace %d: ", format_white_space, indent, face_id); + if (udp_face->hdrs.ip4.ip.ip_version_and_header_length == ipv) + { + s = format (s, "type UDP local %U|%u", + format_ip4_address, &udp_face->hdrs.ip4.ip.src_address, + clib_net_to_host_u16 (udp_face->hdrs.ip4.udp.src_port)); + s = + format (s, " local %U|%u", format_ip4_address, + &udp_face->hdrs.ip4.ip.dst_address, + clib_net_to_host_u16 (udp_face->hdrs.ip4.udp.dst_port)); + s = + format (s, " dev %U", format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, face->shared.sw_if)); + if ((face->shared.flags & HICN_FACE_FLAGS_DELETED)) + s = format (s, " (deleted)"); + } + else + { + s = format (s, "type UDP local %U|%u", + format_ip6_address, &udp_face->hdrs.ip6.ip.src_address, + clib_net_to_host_u16 (udp_face->hdrs.ip6.udp.src_port)); + s = + format (s, " remote %U|%u", format_ip6_address, + &udp_face->hdrs.ip6.ip.dst_address, + clib_net_to_host_u16 (udp_face->hdrs.ip6.udp.dst_port)); + s = + format (s, " dev %U", format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, face->shared.sw_if)); + if ((face->shared.flags & HICN_FACE_FLAGS_DELETED)) + s = format (s, " (deleted)"); + } + } + + return s; +} + +void +hicn_face_udp_get_dpo (hicn_face_t * face, dpo_id_t * dpo) +{ + hicn_face_udp_t *face_udp = (hicn_face_udp_t *) face->data; + u8 version = + (face_udp->hdrs.ip4.ip.ip_version_and_header_length & 0xf0) >> 4; + return hicn_dpo_udp_create_from_face (face, dpo, + version == + (u8) 4 ? strategy_face_udp4_vlib_edge + : strategy_face_udp6_vlib_edge); +} + +hicn_face_vft_t udp_vft = { + .format_face = format_hicn_face_udp, + .hicn_face_del = hicn_face_udp_del, + .hicn_face_get_dpo = hicn_face_udp_get_dpo, +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/faces/udp/face_udp.h b/hicn-plugin/src/faces/udp/face_udp.h new file mode 100755 index 000000000..8694bad5c --- /dev/null +++ b/hicn-plugin/src/faces/udp/face_udp.h @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_FACE_UDP_H__ +#define __HICN_FACE_UDP_H__ + +#include +#include +#include +#include + +#include "../face.h" + +/** + * @file + * @brief UDP face + * + * This file containes the definition of UDP faces. + * UDP faces encap and decap an hicn packet into a UDP tunnel. + * Src and dst address in interest and data packets are not considered and + * should be set to 0 (not checked in the forwarder). + */ + +/* Pre-instantiated ip header to fast fill an newly encapsulated packet */ +extern ip4_header_t ip4_header_skl; +extern ip6_header_t ip6_header_skl; + +#define INVALID_UDP_DPO_INDEX ~0 + +/** + * @brief UDP face representation. The following is stored in the data field of + * an hicn_face_t object (see hicn_face.h). A UDP face is identifies by the + * quadruplet (src addr, dst addr, src port, dst port). + */ +typedef struct hicn_face_udp_t_ +{ + /** + * The headers to paint, in packet painting order + */ + union + { + struct + { + ip4_header_t ip; + udp_header_t udp; + } __attribute__ ((packed)) ip4; + struct + { + ip6_header_t ip; + udp_header_t udp; + } __attribute__ ((packed)) ip6; + } __attribute__ ((packed)) hdrs; +} hicn_face_udp_t; + +/* Hast table mapping the udp key with the face id (dpoi_index pointing to and + element in the face pool defined in hicn_face.h)*/ +extern mhash_t hicn_face_udp_hashtb; + +/** + * @brief Hash table key. + */ +typedef struct hicn_face_udp_key_s +{ + ip46_address_t local_addr; + ip46_address_t remote_addr; + u16 local_port; + u16 remote_port; +} hicn_face_udp_key_t; + +/* DPO type for the udp face */ +extern dpo_type_t hicn_face_udp_type; + +/* VFT table for the udp face. Mainly used to format the face in the right way */ +extern hicn_face_vft_t udp_vft; + +/** + * @brief Create the key object for the mhash. Fill in the key object with the + * expected values. + * + * @param local_addr Local address of the UDP tunnel + * @param remote_addr Remote address of the UDP tunnel + * @param local_port Local port of the UDP tunnel + * @param remote_port Remote port of the UDP tunnel + * @param key Pointer to an allocated hicn_face_udp_key_t object + */ +always_inline void +hicn_face_udp4_get_key (const ip4_address_t * local_addr, + const ip4_address_t * remote_addr, + u16 local_port, u16 remote_port, + hicn_face_udp_key_t * key) +{ + + ip46_address_set_ip4 (&(key->local_addr), local_addr); + ip46_address_set_ip4 (&(key->remote_addr), remote_addr); + key->local_port = local_port; + key->remote_port = remote_port; +} + +/** + * @brief Create the key object for the mhash. Fill in the key object with the + * expected values. + * + * @param local_addr Local address of the UDP tunnel + * @param remote_addr Remote address of the UDP tunnel + * @param local_port Local port of the UDP tunnel + * @param remote_port Remote port of the UDP tunnel + * @param key Pointer to an allocated hicn_face_udp_key_t object + */ +always_inline void +hicn_face_udp6_get_key (const ip6_address_t * local_addr, + const ip6_address_t * remote_addr, + u16 local_port, u16 remote_port, + hicn_face_udp_key_t * key) +{ + key->local_addr.ip6 = *local_addr; + key->remote_addr.ip6 = *remote_addr; + key->local_port = local_port; + key->remote_port = remote_port; +} + +/** + * @brief Get the dpoi from the quadruplet that identifies the face. Does not add any lock. + * + * @param local_addr Local address of the UDP tunnel + * @param remote_addr Remote address of the UDP tunnel + * @param local_port Local port of the UDP tunnel + * @param remote_port Remote port of the UDP tunnel + * + * @result Pointer to the face. + */ +always_inline hicn_face_t * +hicn_face_udp4_get (const ip4_address_t * local_addr, + const ip4_address_t * remote_addr, + u16 local_port, u16 remote_port) +{ + hicn_face_udp_key_t key; + + hicn_face_udp4_get_key (local_addr, remote_addr, local_port, remote_port, + &key); + + hicn_face_id_t *dpoi_index = + (hicn_face_id_t *) mhash_get (&hicn_face_udp_hashtb, + &key); + + return dpoi_index == NULL ? NULL : hicn_dpoi_get_from_idx (*dpoi_index); +} + +/** + * @brief Get the dpoi from the quadruplet that identifies the face. Does not add any lock. + * + * @param local_addr Local address of the UDP tunnel (network order) + * @param remote_addr Remote address of the UDP tunnel (network order) + * @param local_port Local port of the UDP tunnel (network order) + * @param remote_port Remote port of the UDP tunnel (network order) + * + * @result Pointer to the face. + */ +always_inline hicn_face_t * +hicn_face_udp6_get (const ip6_address_t * local_addr, + const ip6_address_t * remote_addr, + u16 local_port, u16 remote_port) +{ + hicn_face_udp_key_t key; + + hicn_face_udp6_get_key (local_addr, remote_addr, local_port, remote_port, + &key); + + hicn_face_id_t *dpoi_index = + (hicn_face_id_t *) mhash_get (&hicn_face_udp_hashtb, + &key); + + return dpoi_index == NULL ? NULL : hicn_dpoi_get_from_idx (*dpoi_index); +} + + +/** + * @brief Initialize the udp face module + */ +void hicn_face_udp_init (vlib_main_t * vm); + +/** + * @brief Create a new face ip. API for other modules (e.g., routing) + * + * @param local_addr Local ip v4 or v6 address of the face (network order) + * @param remote_addr Remote ip v4 or v6 address of the face (network order) + * @param local_port Local udp port of the face (network order) + * @param remote_port Remote udp port of the face (network order) + * @param sw_if interface associated to the face + * @param pfaceid Pointer to return the face id + * @return HICN_ERROR_FACE_NO_GLOBAL_IP if the face does not have a globally + * reachable ip address, otherwise HICN_ERROR_NONE + */ +int hicn_face_udp_add (const ip46_address_t * local_addr, + const ip46_address_t * remote_addr, u16 local_port, + u16 remote_port, u32 swif, hicn_face_id_t * pfaceid); + +/** + * @brief Delete an ip face + * + * @param face_id Id of the face to delete + * @return HICN_ERROR_FACE_NOT_FOUND if the face does not exist, otherwise + * HICN_ERROR_NONE + */ +int hicn_face_udp_del (hicn_face_id_t faceid); + +/** + * @brief Format a UDP face + * + * @param s Pointer to a previous string. If null it will be initialize + * @param args Array storing input values. Expected u32 indent and u32 face_id + * @return String with the formatted face + */ +u8 *format_hicn_face_udp (u8 * s, va_list * args); + +/** + * @brief Create a dpo from a udp face + * + * @param face Face from which to create the dpo + * @return the dpo + */ +void hicn_face_udp_get_dpo (hicn_face_t * face, dpo_id_t * dpo); + +/** + * @brief Init some internal structures + */ +void hicn_face_udp_init_internal (void); + +#endif // __HICN_FACE_UDP_H__ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/faces/udp/face_udp_cli.c b/hicn-plugin/src/faces/udp/face_udp_cli.c new file mode 100755 index 000000000..7bb172ce8 --- /dev/null +++ b/hicn-plugin/src/faces/udp/face_udp_cli.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "face_udp.h" +#include "dpo_udp.h" + +#include +#include +#include +#include + +#define HICN_FACE_NONE 0 +#define HICN_FACE_DELETE 1 +#define HICN_FACE_ADD 2 + + +static clib_error_t * +hicn_face_udp_cli_set_command_fn (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + vnet_main_t *vnm = vnet_get_main (); + ip46_address_t src_addr; + u32 src_port = 0; + ip46_address_t dst_addr; + u32 dst_port = 0; + hicn_face_id_t face_id = HICN_FACE_NULL; + int ret = HICN_ERROR_NONE; + int sw_if; + int face_op = HICN_FACE_NONE; + + ip46_address_reset (&src_addr); + ip46_address_reset (&dst_addr); + /* Get a line of input. */ + unformat_input_t _line_input, *line_input = &_line_input; + if (!unformat_user (main_input, unformat_line_input, line_input)) + { + return (0); + } + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "del")) + { + if (unformat (line_input, "id %d", &face_id)) + face_op = HICN_FACE_DELETE; + else + { + return clib_error_return (0, "missing face id"); + } + } + else if (unformat (line_input, "add")) + { + face_op = HICN_FACE_ADD; + if (unformat + (line_input, "src_addr %U port %u dst_addr %U port %u intfc %U", + unformat_ip46_address, &src_addr, IP46_TYPE_ANY, &src_port, + unformat_ip46_address, &dst_addr, IP46_TYPE_ANY, &dst_port, + unformat_vnet_sw_interface, vnm, &sw_if)); + else + { + return clib_error_return (0, "%s '%U'", + get_error_string + (HICN_ERROR_CLI_INVAL), + format_unformat_error, line_input); + } + } + else + { + return clib_error_return (0, "%s '%U'", + get_error_string (HICN_ERROR_CLI_INVAL), + format_unformat_error, line_input); + } + } + + if (face_id != HICN_FACE_NULL) + { + if (!hicn_dpoi_idx_is_valid (face_id)) + { + return clib_error_return (0, "%s, face_id %d not valid", + get_error_string (ret), face_id); + } + } + + int rv; + switch (face_op) + { + case HICN_FACE_ADD: + + /* Check for presence of next hop address */ + if (((dst_addr.as_u64[0] == (u64) 0) && (dst_addr.as_u64[1] == (u64) 0)) + || dst_port == 0) + { + return clib_error_return (0, "dst address and port not specified"); + } + + if (((src_addr.as_u64[0] == (u64) 0) && (src_addr.as_u64[1] == (u64) 0)) + || src_port == 0) + { + return clib_error_return (0, "src address not specified"); + } + + rv = + hicn_face_udp_add (&src_addr, &dst_addr, + clib_host_to_net_u16 (src_port), + clib_host_to_net_u16 (dst_port), sw_if, &face_id); + if (rv == HICN_ERROR_NONE) + { + vlib_cli_output (vm, "Face id: %d", face_id); + } + else + { + return clib_error_return (0, get_error_string (rv)); + } + break; + case HICN_FACE_DELETE: + rv = hicn_face_udp_del (face_id); + if (rv == HICN_ERROR_NONE) + { + vlib_cli_output (vm, "Face %d deleted", face_id); + } + else + { + return clib_error_return (0, get_error_string (rv)); + } + break; + default: + return clib_error_return (0, "Operation (%d) not implemented", face_op); + break; + } + return (rv == HICN_ERROR_NONE) ? 0 : clib_error_return (0, "%s\n", + get_error_string + (rv)); +} + +/* cli declaration for 'cfg face' */ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (hicn_face_udp_cli_set_command, static) = +{ + .path = "hicn face udp", + .short_help = "hicn face udp {add src_addr port dst_addr port } intfc | {del id }", + .function = hicn_face_udp_cli_set_command_fn, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/faces/udp/face_udp_node.c b/hicn-plugin/src/faces/udp/face_udp_node.c new file mode 100755 index 000000000..74d0b1864 --- /dev/null +++ b/hicn-plugin/src/faces/udp/face_udp_node.c @@ -0,0 +1,864 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "face_udp.h" +#include "face_udp_node.h" +#include "dpo_udp.h" +#include "../face.h" +#include "../../strategy.h" +#include "../../strategy_dpo_manager.h" +#include "../../hicn.h" + +/** + * @File + * + * Definition of the nodes for udp faces. + */ + +vlib_node_registration_t hicn_face_udp4_input_node; +vlib_node_registration_t hicn_face_udp6_input_node; +vlib_node_registration_t hicn_face_udp4_output_node; +vlib_node_registration_t hicn_face_udp6_output_node; + +static char *hicn_face_udp4_input_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + +static char *hicn_face_udp6_input_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; +} hicn_face_udp4_input_trace_t; + +typedef enum +{ + HICN_FACE_UDP4_INPUT_NEXT_DATA, + HICN_FACE_UDP4_INPUT_NEXT_MAPME, + HICN_FACE_UDP4_INPUT_NEXT_ERROR_DROP, + HICN_FACE_UDP4_INPUT_N_NEXT, +} hicn_face_udp4_input_next_t; + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; +} hicn_face_udp6_input_trace_t; + +typedef enum +{ + HICN_FACE_UDP6_INPUT_NEXT_DATA, + HICN_FACE_UDP6_INPUT_NEXT_MAPME, + HICN_FACE_UDP6_INPUT_NEXT_ERROR_DROP, + HICN_FACE_UDP6_INPUT_N_NEXT, +} hicn_face_udp6_input_next_t; + +#define ERROR_INPUT_UDP4 HICN_FACE_UDP4_INPUT_NEXT_ERROR_DROP +#define ERROR_INPUT_UDP6 HICN_FACE_UDP6_INPUT_NEXT_ERROR_DROP + +#define NEXT_MAPME_UDP4 HICN_FACE_UDP4_INPUT_NEXT_MAPME +#define NEXT_MAPME_UDP6 HICN_FACE_UDP6_INPUT_NEXT_MAPME +#define NEXT_DATA_UDP4 HICN_FACE_UDP4_INPUT_NEXT_DATA +#define NEXT_DATA_UDP6 HICN_FACE_UDP6_INPUT_NEXT_DATA + +#define IP_HEADER_4 ip4_header_t +#define IP_HEADER_6 ip6_header_t + +#define HICN_DPO_UDP_LOCK_IP4 hicn_dpo_udp4_lock +#define HICN_DPO_UDP_LOCK_IP6 hicn_dpo_udp6_lock + +#define TRACE_INPUT_PKT_UDP4 hicn_face_udp4_input_trace_t +#define TRACE_INPUT_PKT_UDP6 hicn_face_udp6_input_trace_t + +#define face_input_x1(ipv) \ + do { \ + int ret; \ + vlib_buffer_t *b0; \ + u32 bi0; \ + u32 next0 = ERROR_INPUT_UDP##ipv; \ + IP_HEADER_##ipv * ip_hdr = NULL; \ + u8 * inner_ip_hdr = NULL; \ + udp_header_t * udp_hdr = NULL; \ + hicn_buffer_t * hicnb0; \ + /* Prefetch for next iteration. */ \ + if (n_left_from > 1) \ + { \ + vlib_buffer_t *b1; \ + b1 = vlib_get_buffer (vm, from[1]); \ + CLIB_PREFETCH (b1, CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b1->data, CLIB_CACHE_LINE_BYTES , LOAD); \ + } \ + /* Dequeue a packet buffer */ \ + bi0 = from[0]; \ + from += 1; \ + n_left_from -= 1; \ + to_next[0] = bi0; \ + to_next += 1; \ + n_left_to_next -= 1; \ + \ + b0 = vlib_get_buffer (vm, bi0); \ + ip_hdr = (IP_HEADER_##ipv *) vlib_buffer_get_current(b0); \ + udp_hdr = (udp_header_t *) (ip_hdr + 1); \ + hicnb0 = hicn_get_buffer(b0); \ + \ + inner_ip_hdr = (u8 *)(udp_hdr + 1); \ + u8 is_v6 = ((inner_ip_hdr[0] & 2) >> 1); \ + u8 is_icmp = is_v6*(inner_ip_hdr[7] == IPPROTO_ICMPV6) + \ + (1 - is_v6)*(inner_ip_hdr[10] == IPPROTO_ICMPV4); \ + \ + ret = HICN_DPO_UDP_LOCK_IP##ipv \ + (&(hicnb0->face_dpo_id), \ + &(ip_hdr->dst_address), \ + &(ip_hdr->src_address), \ + (udp_hdr->dst_port), \ + (udp_hdr->src_port), \ + &hicnb0->is_appface); \ + \ + if ( PREDICT_FALSE(ret != HICN_ERROR_NONE) ) \ + { \ + next0 = ERROR_INPUT_UDP##ipv; \ + } \ + else \ + { \ + next0 = is_icmp*NEXT_MAPME_UDP##ipv + \ + (1-is_icmp)*NEXT_DATA_UDP##ipv; \ + stats.pkts_data_count += 1; \ + \ + vlib_buffer_advance(b0, sizeof(IP_HEADER_##ipv) + \ + sizeof(udp_header_t)); \ + } \ + \ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && \ + (b0->flags & VLIB_BUFFER_IS_TRACED))) \ + { \ + TRACE_INPUT_PKT_UDP##ipv *t = \ + vlib_add_trace (vm, node, b0, sizeof (*t)); \ + t->pkt_type = HICN_PKT_TYPE_CONTENT; \ + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; \ + t->next_index = next0; \ + } \ + \ + \ + /* Verify speculative enqueue, maybe switch current next frame */ \ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, \ + to_next, n_left_to_next, \ + bi0, next0); \ + }while(0) \ + +#define face_input_x2(ipv) \ + do { \ + int ret0, ret1; \ + vlib_buffer_t *b0, *b1; \ + u32 bi0, bi1; \ + u32 next0 = ERROR_INPUT_UDP##ipv; \ + u32 next1 = ERROR_INPUT_UDP##ipv; \ + IP_HEADER_##ipv * ip_hdr0 = NULL; \ + IP_HEADER_##ipv * ip_hdr1 = NULL; \ + u8 * inner_ip_hdr0 = NULL; \ + u8 * inner_ip_hdr1 = NULL; \ + udp_header_t * udp_hdr0 = NULL; \ + udp_header_t * udp_hdr1 = NULL; \ + hicn_buffer_t *hicnb0, *hicnb1; \ + \ + /* Prefetch for next iteration. */ \ + { \ + vlib_buffer_t *b2, *b3; \ + b2 = vlib_get_buffer (vm, from[2]); \ + b3 = vlib_get_buffer (vm, from[3]); \ + CLIB_PREFETCH (b2, CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b3, CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b2->data, CLIB_CACHE_LINE_BYTES , LOAD); \ + CLIB_PREFETCH (b3->data, CLIB_CACHE_LINE_BYTES , LOAD); \ + } \ + \ + /* Dequeue a packet buffer */ \ + bi0 = from[0]; \ + bi1 = from[1]; \ + from += 2; \ + n_left_from -= 2; \ + to_next[0] = bi0; \ + to_next[1] = bi1; \ + to_next += 2; \ + n_left_to_next -= 2; \ + \ + b0 = vlib_get_buffer (vm, bi0); \ + b1 = vlib_get_buffer (vm, bi1); \ + ip_hdr0 = (IP_HEADER_##ipv *) vlib_buffer_get_current(b0); \ + ip_hdr1 = (IP_HEADER_##ipv *) vlib_buffer_get_current(b1); \ + udp_hdr0 = (udp_header_t *) (ip_hdr0 + 1); \ + udp_hdr1 = (udp_header_t *) (ip_hdr1 + 1); \ + hicnb0 = hicn_get_buffer(b0); \ + hicnb1 = hicn_get_buffer(b1); \ + \ + inner_ip_hdr0 = (u8 *)(udp_hdr0 + 1); \ + u8 is_v6_0 = ((inner_ip_hdr0[0] & 2) >> 1); \ + u8 is_icmp0 = is_v6_0*(inner_ip_hdr0[7] == IPPROTO_ICMPV6) + \ + (1 - is_v6_0)*(inner_ip_hdr0[10] == IPPROTO_ICMPV4); \ + \ + inner_ip_hdr1 = (u8 *)(udp_hdr1 + 1); \ + u8 is_v6_1 = ((inner_ip_hdr1[0] & 2) >> 1); \ + u8 is_icmp1 = is_v6_1*(inner_ip_hdr1[7] == IPPROTO_ICMPV6) + \ + (1 - is_v6_1)*(inner_ip_hdr1[10] == IPPROTO_ICMPV4); \ + \ + ret0 = HICN_DPO_UDP_LOCK_IP##ipv \ + (&(hicnb0->face_dpo_id), \ + &(ip_hdr0->dst_address), \ + &(ip_hdr0->src_address), \ + (udp_hdr0->dst_port), \ + (udp_hdr0->src_port), \ + &hicnb0->is_appface); \ + \ + ret1 = HICN_DPO_UDP_LOCK_IP##ipv \ + (&(hicnb1->face_dpo_id), \ + &(ip_hdr1->dst_address), \ + &(ip_hdr1->src_address), \ + (udp_hdr1->dst_port), \ + (udp_hdr1->src_port), \ + &hicnb1->is_appface); \ + \ + if ( PREDICT_FALSE(ret0 != HICN_ERROR_NONE) ) \ + { \ + next0 = ERROR_INPUT_UDP##ipv; \ + } \ + else \ + { \ + stats.pkts_data_count += 1; \ + next0 = is_icmp0*NEXT_MAPME_UDP##ipv + \ + (1-is_icmp0)*NEXT_DATA_UDP##ipv; \ + \ + vlib_buffer_advance(b0, sizeof(IP_HEADER_##ipv) + \ + sizeof(udp_header_t)); \ + } \ + \ + if ( PREDICT_FALSE(ret1 != HICN_ERROR_NONE) ) \ + { \ + next1 = ERROR_INPUT_UDP##ipv; \ + } \ + else \ + { \ + stats.pkts_data_count += 1; \ + next1 = is_icmp1*NEXT_MAPME_UDP##ipv + \ + (1-is_icmp1)*NEXT_DATA_UDP##ipv; \ + \ + vlib_buffer_advance(b1, sizeof(IP_HEADER_##ipv) + \ + sizeof(udp_header_t)); \ + } \ + \ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && \ + (b0->flags & VLIB_BUFFER_IS_TRACED))) \ + { \ + TRACE_INPUT_PKT_UDP##ipv *t = \ + vlib_add_trace (vm, node, b0, sizeof (*t)); \ + t->pkt_type = HICN_PKT_TYPE_CONTENT; \ + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; \ + t->next_index = next0; \ + } \ + \ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && \ + (b1->flags & VLIB_BUFFER_IS_TRACED))) \ + { \ + TRACE_INPUT_PKT_UDP##ipv *t = \ + vlib_add_trace (vm, node, b1, sizeof (*t)); \ + t->pkt_type = HICN_PKT_TYPE_CONTENT; \ + t->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX]; \ + t->next_index = next1; \ + } \ + \ + \ + /* Verify speculative enqueue, maybe switch current next frame */ \ + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, \ + to_next, n_left_to_next, \ + bi0, bi1, next0, next1); \ + }while(0) \ + +static uword +hicn_face_udp4_input_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next, next_index; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + + while (n_left_from > 0) + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + /* Dual loop, X2 */ + while (n_left_from >= 4 && n_left_to_next >= 2) + { + face_input_x2 (4); + } + + /* Dual loop, X1 */ + while (n_left_from > 0 && n_left_to_next > 0) + { + face_input_x1 (4); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, node->node_index, + HICNFWD_ERROR_DATAS, stats.pkts_data_count); + + return (frame->n_vectors); +} + +/* packet trace format function */ +static u8 * +hicn_face_udp4_input_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_face_udp4_input_trace_t *t = + va_arg (*args, hicn_face_udp4_input_trace_t *); + + s = format (s, "FACE_UDP4_INPUT: pkt: %d, sw_if_index %d, next index %d", + (int) t->pkt_type, t->sw_if_index, t->next_index); + return (s); +} + +/* + * Node registration for the interest forwarder node + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (hicn_face_udp4_input_node) = +{ + .function = hicn_face_udp4_input_node_fn, + .name = "hicn-face-udp4-input", + .vector_size = sizeof (u32), + .format_trace = hicn_face_udp4_input_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (hicn_face_udp4_input_error_strings), + .error_strings = hicn_face_udp4_input_error_strings, + .n_next_nodes = HICN_FACE_UDP4_INPUT_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = + { + [HICN_FACE_UDP4_INPUT_NEXT_DATA] = "hicn-data-pcslookup", + [HICN_FACE_UDP4_INPUT_NEXT_MAPME] = "hicn-mapme-ack", + [HICN_FACE_UDP4_INPUT_NEXT_ERROR_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + + +static uword +hicn_face_udp6_input_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next, next_index; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + + while (n_left_from > 0) + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + /* Dual loop, X2 */ + while (n_left_from >= 4 && n_left_to_next >= 2) + { + face_input_x2 (6); + } + + /* Dual loop, X1 */ + while (n_left_from > 0 && n_left_to_next > 0) + { + face_input_x1 (6); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, node->node_index, + HICNFWD_ERROR_PROCESSED, stats.pkts_processed); + + vlib_node_increment_counter (vm, node->node_index, + HICNFWD_ERROR_DATAS, stats.pkts_data_count); + + return (frame->n_vectors); +} + +/* packet trace format function */ +static u8 * +hicn_face_udp6_input_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_face_udp6_input_trace_t *t = + va_arg (*args, hicn_face_udp6_input_trace_t *); + + s = format (s, "FACE_UDP6_INPUT: pkt: %d, sw_if_index %d, next index %d", + (int) t->pkt_type, t->sw_if_index, t->next_index); + return (s); +} + +/* + * Node registration for the interest forwarder node + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (hicn_face_udp6_input_node) = +{ + .function = hicn_face_udp6_input_node_fn, + .name = "hicn-face-udp6-input", + .vector_size = sizeof (u32), + .format_trace = hicn_face_udp6_input_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (hicn_face_udp6_input_error_strings), + .error_strings = hicn_face_udp6_input_error_strings, + .n_next_nodes = HICN_FACE_UDP6_INPUT_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = + { + [HICN_FACE_UDP6_INPUT_NEXT_DATA] = "hicn-data-pcslookup", + [HICN_FACE_UDP6_INPUT_NEXT_MAPME] = "hicn-mapme-ack", + [HICN_FACE_UDP6_INPUT_NEXT_ERROR_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +/******* Face Output *******/ + +always_inline void +hicn_face_udp4_encap (vlib_main_t * vm, + vlib_buffer_t * outer_b0, + hicn_face_t * face, u32 * next) +{ + u16 old_l0 = 0, new_l0; + ip_csum_t sum0; + ip4_header_t *ip0; + udp_header_t *udp0; + hicn_face_udp_t *face_udp = (hicn_face_udp_t *) face->data; + ip_adjacency_t *adj = adj_get (face->shared.adj); + + /* ip */ + ip0 = vlib_buffer_get_current (outer_b0); + clib_memcpy (ip0, &(face_udp->hdrs.ip4.ip), sizeof (ip4_header_t) + + sizeof (udp_header_t)); + + /* Fix UDP length */ + udp0 = (udp_header_t *) (ip0 + 1); + + new_l0 = + clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, outer_b0) - + sizeof (*ip0)); + udp0->length = new_l0; + + old_l0 = ip0->length; + ip0->length = + clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, outer_b0)); + + sum0 = ip0->checksum; + + //old_l0 always 0, see the rewrite setup + new_l0 = ip0->length; + + sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t, + length /* changed member */ ); + ip0->checksum = sum0; + + vnet_buffer (outer_b0)->ip.adj_index[VLIB_TX] = face->shared.adj; + + *next = adj->lookup_next_index; +} + +always_inline void +hicn_face_udp6_encap (vlib_main_t * vm, + vlib_buffer_t * outer_b0, + hicn_face_t * face, u32 * next) +{ + int bogus0; + u16 new_l0; + ip6_header_t *ip0; + udp_header_t *udp0; + hicn_face_udp_t *face_udp = (hicn_face_udp_t *) face->data; + ip_adjacency_t *adj = adj_get (face->shared.adj); + + /* ip */ + ip0 = vlib_buffer_get_current (outer_b0); + clib_memcpy (ip0, &(face_udp->hdrs.ip6.ip), sizeof (ip6_header_t) + + sizeof (udp_header_t)); + new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, outer_b0) + - sizeof (*ip0)); + ip0->payload_length = new_l0; + + /* Fix UDP length */ + udp0 = (udp_header_t *) (ip0 + 1); + udp0->length = new_l0; + + udp0->checksum = + ip6_tcp_udp_icmp_compute_checksum (vm, outer_b0, ip0, &bogus0); + + ASSERT (bogus0 == 0); + + if (udp0->checksum == 0) + udp0->checksum = 0xffff; + + vnet_buffer (outer_b0)->ip.adj_index[VLIB_TX] = face->shared.adj; + + *next = adj->lookup_next_index; +} + +static char *hicn_face_udp4_output_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + +static char *hicn_face_udp6_output_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; +} hicn_face_udp4_output_trace_t; + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; +} hicn_face_udp6_output_trace_t; + +#define HICN_FACE_UDP_ENCAP_IP4 hicn_face_udp4_encap +#define HICN_FACE_UDP_ENCAP_IP6 hicn_face_udp6_encap + +#define TRACE_OUTPUT_PKT_UDP4 hicn_face_udp4_output_trace_t +#define TRACE_OUTPUT_PKT_UDP6 hicn_face_udp6_output_trace_t + +#define IP_HEADER_4 ip4_header_t +#define IP_HEADER_6 ip6_header_t + +#define face_output_x1(ipv) \ + do { \ + vlib_buffer_t *b0; \ + u32 bi0; \ + u32 next0 = IP_LOOKUP_NEXT_DROP; \ + hicn_face_t * face; \ + \ + /* Prefetch for next iteration. */ \ + if (n_left_from > 1) \ + { \ + vlib_buffer_t *b1; \ + b1 = vlib_get_buffer (vm, from[1]); \ + CLIB_PREFETCH (b1, CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b1->data, CLIB_CACHE_LINE_BYTES , LOAD); \ + } \ + /* Dequeue a packet buffer */ \ + bi0 = from[0]; \ + from += 1; \ + n_left_from -= 1; \ + \ + b0 = vlib_get_buffer (vm, bi0); \ + face = \ + hicn_dpoi_get_from_idx(vnet_buffer (b0)->ip.adj_index[VLIB_TX]); \ + \ + if (PREDICT_TRUE(face != NULL)) \ + { \ + /* Adjust vlib buffer. Create space for the udp tunnel. */ \ + vlib_buffer_advance(b0, -(sizeof (IP_HEADER_##ipv) + \ + sizeof (udp_header_t))); \ + \ + \ + HICN_FACE_UDP_ENCAP_IP##ipv \ + (vm, b0, face, &next0); \ + stats.pkts_interest_count += 1; \ + } \ + \ + \ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && \ + (b0->flags & VLIB_BUFFER_IS_TRACED))) \ + { \ + TRACE_OUTPUT_PKT_UDP##ipv *t = \ + vlib_add_trace (vm, node, b0, sizeof (*t)); \ + t->pkt_type = HICN_PKT_TYPE_INTEREST; \ + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; \ + t->next_index = next0; \ + } \ + \ + to_next[0] = bi0; \ + to_next += 1; \ + n_left_to_next -= 1; \ + \ + \ + /* Verify speculative enqueue, maybe switch current next frame */ \ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, \ + to_next, n_left_to_next, \ + bi0, next0); \ + } while(0) \ + + +#define face_output_x2(ipv) \ + do { \ + vlib_buffer_t *b0, *b1; \ + u32 bi0, bi1; \ + u32 next0 = IP_LOOKUP_NEXT_DROP; \ + u32 next1 = IP_LOOKUP_NEXT_DROP; \ + hicn_face_t *face0, *face1; \ + \ + /* Prefetch for next iteration. */ \ + { \ + vlib_buffer_t *b2, *b3; \ + b2 = vlib_get_buffer (vm, from[2]); \ + b3 = vlib_get_buffer (vm, from[3]); \ + CLIB_PREFETCH (b2, CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b3, CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b2->data, CLIB_CACHE_LINE_BYTES , LOAD); \ + CLIB_PREFETCH (b3->data, CLIB_CACHE_LINE_BYTES , LOAD); \ + } \ + \ + /* Dequeue a packet buffer */ \ + bi0 = from[0]; \ + bi1 = from[1]; \ + from += 2; \ + n_left_from -= 2; \ + to_next[0] = bi0; \ + to_next[1] = bi1; \ + to_next += 2; \ + n_left_to_next -= 2; \ + \ + b0 = vlib_get_buffer (vm, bi0); \ + b1 = vlib_get_buffer (vm, bi1); \ + \ + face0 = \ + hicn_dpoi_get_from_idx(vnet_buffer (b0)->ip.adj_index[VLIB_TX]); \ + face1 = \ + hicn_dpoi_get_from_idx(vnet_buffer (b1)->ip.adj_index[VLIB_TX]); \ + \ + if (PREDICT_TRUE(face0 != NULL)) \ + { \ + /* Adjust vlib buffer. Create space for the udp tunnel. */ \ + vlib_buffer_advance(b0, -(sizeof (IP_HEADER_##ipv) + \ + sizeof (udp_header_t))); \ + \ + \ + HICN_FACE_UDP_ENCAP_IP##ipv \ + (vm, b0, face0, &next0); \ + stats.pkts_interest_count += 1; \ + } \ + \ + if (PREDICT_TRUE(face1 != NULL)) \ + { \ + /* Adjust vlib buffer. Create space for the udp tunnel. */ \ + vlib_buffer_advance(b1, -(sizeof (IP_HEADER_##ipv) + \ + sizeof (udp_header_t))); \ + \ + \ + HICN_FACE_UDP_ENCAP_IP##ipv \ + (vm, b1, face1, &next1); \ + stats.pkts_interest_count += 1; \ + } \ + \ + \ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && \ + (b0->flags & VLIB_BUFFER_IS_TRACED))) \ + { \ + TRACE_OUTPUT_PKT_UDP##ipv *t = \ + vlib_add_trace (vm, node, b0, sizeof (*t)); \ + t->pkt_type = HICN_PKT_TYPE_INTEREST; \ + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; \ + t->next_index = next0; \ + } \ + \ + /* Verify speculative enqueue, maybe switch current next frame */ \ + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, \ + to_next, n_left_to_next, \ + bi0, bi1, next0, next1); \ + } while(0) \ + + +static uword +hicn_face_udp4_output_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next, next_index; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + /* Dual loop, X2 */ + while (n_left_from >= 4 && n_left_to_next >= 2) + { + face_output_x2 (4); + } + + /* Dual loop, X1 */ + while (n_left_from > 0 && n_left_to_next > 0) + { + face_output_x1 (4); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, node->node_index, + HICNFWD_ERROR_INTERESTS, + stats.pkts_interest_count); + + return (frame->n_vectors); +} + +/* packet trace format function */ +static u8 * +hicn_face_udp4_output_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_face_udp4_output_trace_t *t = + va_arg (*args, hicn_face_udp4_output_trace_t *); + + s = format (s, "FACE_UDP4_OUTPUT: pkt: %d, sw_if_index %d, next index %d", + (int) t->pkt_type, t->sw_if_index, t->next_index); + return (s); +} + +/* *INDENT-OFF* */ +/* + * Node registration for the interest forwarder node + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (hicn_face_udp4_output_node) = +{ + .function = hicn_face_udp4_output_node_fn, + .name = "hicn-face-udp4-output", + .vector_size = sizeof (u32), + .format_trace = hicn_face_udp4_output_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (hicn_face_udp4_output_error_strings), + .error_strings = hicn_face_udp4_output_error_strings, + .n_next_nodes = IP4_LOOKUP_N_NEXT, + /* Reusing the list of nodes from lookup to be compatible with arp */ + .next_nodes = IP4_LOOKUP_NEXT_NODES, +}; +/* *INDENT-ON* */ + +/* *INDENT-ON* */ + +static uword +hicn_face_udp6_output_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next, next_index; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + /* Dual loop, X2 */ + while (n_left_from >= 4 && n_left_to_next >= 2) + { + face_output_x2 (6); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + face_output_x1 (6); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, node->node_index, + HICNFWD_ERROR_INTERESTS, + stats.pkts_interest_count); + + return (frame->n_vectors); +} + +/* packet trace format function */ +static u8 * +hicn_face_udp6_output_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_face_udp6_output_trace_t *t = + va_arg (*args, hicn_face_udp6_output_trace_t *); + + s = format (s, "FACE_UDP6_OUTPUT: pkt: %d, sw_if_index %d, next index %d", + (int) t->pkt_type, t->sw_if_index, t->next_index); + return (s); +} + +/* *INDENT-OFF* */ +/* + * Node registration for the interest forwarder node + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (hicn_face_udp6_output_node) = +{ + .function = hicn_face_udp6_output_node_fn, + .name = "hicn-face-udp6-output", + .vector_size = sizeof (u32), + .format_trace = hicn_face_udp6_output_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (hicn_face_udp6_output_error_strings), + .error_strings = hicn_face_udp6_output_error_strings, + .n_next_nodes = IP6_LOOKUP_N_NEXT, + /* Reusing the list of nodes from lookup to be compatible with neighbour discovery */ + .next_nodes = IP6_LOOKUP_NEXT_NODES, +}; +/* *INDENT-ON* */ + +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/faces/udp/face_udp_node.h b/hicn-plugin/src/faces/udp/face_udp_node.h new file mode 100755 index 000000000..c759312c8 --- /dev/null +++ b/hicn-plugin/src/faces/udp/face_udp_node.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_FACE_UDP_NODE_H__ +#define __HICN_FACE_UDP_NODE_H__ + +#include +#include + +extern vlib_node_registration_t hicn_face_udp4_input_node; +extern vlib_node_registration_t hicn_face_udp6_input_node; +extern vlib_node_registration_t hicn_face_udp4_output_node; +extern vlib_node_registration_t hicn_face_udp6_output_node; + +#endif // __HICN_FACE_UDP_NODE_H__ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/faces/udp/iface_udp_node.c b/hicn-plugin/src/faces/udp/iface_udp_node.c new file mode 100755 index 000000000..ddea31b4c --- /dev/null +++ b/hicn-plugin/src/faces/udp/iface_udp_node.c @@ -0,0 +1,894 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "iface_udp_node.h" +#include "dpo_udp.h" +#include "../face.h" + +#include "../../infra.h" +#include "../../hicn.h" + +/** + * @File + * + * Definition of the nodes for udp incomplete faces. + */ + +vlib_node_registration_t hicn_iface_udp4_input_node; +vlib_node_registration_t hicn_iface_udp6_input_node; +vlib_node_registration_t hicn_iface_udp4_output_node; +vlib_node_registration_t hicn_iface_udp6_output_node; + +u32 data_fwd_face_udp4_vlib_edge; +u32 data_fwd_face_udp6_vlib_edge; + +void +hicn_iface_udp_init (vlib_main_t * vm) +{ + data_fwd_face_udp4_vlib_edge = vlib_node_add_next (vm, + hicn_data_fwd_node.index, + hicn_iface_udp4_output_node. + index); + + data_fwd_face_udp6_vlib_edge = vlib_node_add_next (vm, + hicn_data_fwd_node.index, + hicn_iface_udp6_output_node. + index); + + u32 temp_index4 = vlib_node_add_next (vm, + hicn_interest_hitcs_node.index, + hicn_iface_udp4_output_node.index); + u32 temp_index6 = vlib_node_add_next (vm, + hicn_interest_hitcs_node.index, + hicn_iface_udp6_output_node.index); + + ASSERT (temp_index4 == data_fwd_face_udp4_vlib_edge); + ASSERT (temp_index6 == data_fwd_face_udp6_vlib_edge); +} + +static char *hicn_iface_udp4_input_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + +static char *hicn_iface_udp6_input_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + +u32 +get_face_udp4_output_node (void) +{ + return data_fwd_face_udp4_vlib_edge; +} + +u32 +get_face_udp6_output_node (void) +{ + return data_fwd_face_udp6_vlib_edge; +} + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; +} hicn_iface_udp4_input_trace_t; + +typedef enum +{ + HICN_IFACE_UDP4_INPUT_NEXT_INTEREST, + HICN_IFACE_UDP4_INPUT_NEXT_MAPME, + HICN_IFACE_UDP4_INPUT_NEXT_ERROR_DROP, + HICN_IFACE_UDP4_INPUT_N_NEXT, +} hicn_iface_udp4_input_next_t; + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; +} hicn_iface_udp6_input_trace_t; + +typedef enum +{ + HICN_IFACE_UDP6_INPUT_NEXT_INTEREST, + HICN_IFACE_UDP6_INPUT_NEXT_MAPME, + HICN_IFACE_UDP6_INPUT_NEXT_ERROR_DROP, + HICN_IFACE_UDP6_INPUT_N_NEXT, +} hicn_iface_udp6_input_next_t; + +#define ERROR_INPUT_UDP4 HICN_IFACE_UDP4_INPUT_NEXT_ERROR_DROP +#define ERROR_INPUT_UDP6 HICN_IFACE_UDP6_INPUT_NEXT_ERROR_DROP + +#define IP_HEADER_4 ip4_header_t +#define IP_HEADER_6 ip6_header_t + +#define NEXT_MAPME_UDP4 HICN_IFACE_UDP4_INPUT_NEXT_MAPME +#define NEXT_MAPME_UDP6 HICN_IFACE_UDP6_INPUT_NEXT_MAPME + +#define NEXT_INTEREST_UDP4 HICN_IFACE_UDP4_INPUT_NEXT_INTEREST +#define NEXT_INTEREST_UDP6 HICN_IFACE_UDP6_INPUT_NEXT_INTEREST + +#define HICN_IFACE_UDP_ADD_LOCK_IP4 hicn_dpo_udp4_add_and_lock +#define HICN_IFACE_UDP_ADD_LOCK_IP6 hicn_dpo_udp6_add_and_lock + +#define GET_FACE_UDP4 get_face_udp4_output_node +#define GET_FACE_UDP6 get_face_udp6_output_node + +#define TRACE_INPUT_PKT_UDP4 hicn_iface_udp4_input_trace_t +#define TRACE_INPUT_PKT_UDP6 hicn_iface_udp6_input_trace_t + +#define iface_input_x1(ipv) \ + do { \ + vlib_buffer_t *b0; \ + u32 bi0; \ + u32 next0 = ERROR_INPUT_UDP##ipv; \ + IP_HEADER_##ipv * ip_hdr = NULL; \ + u8 * inner_ip_hdr = NULL; \ + udp_header_t * udp_hdr = NULL; \ + hicn_buffer_t * hicnb0; \ + /* Prefetch for next iteration. */ \ + if (n_left_from > 1) \ + { \ + vlib_buffer_t *b1; \ + b1 = vlib_get_buffer (vm, from[1]); \ + CLIB_PREFETCH (b1, CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b1->data, CLIB_CACHE_LINE_BYTES , LOAD); \ + } \ + /* Dequeue a packet buffer */ \ + bi0 = from[0]; \ + from += 1; \ + n_left_from -= 1; \ + to_next[0] = bi0; \ + to_next += 1; \ + n_left_to_next -= 1; \ + \ + b0 = vlib_get_buffer (vm, bi0); \ + ip_hdr = (IP_HEADER_##ipv *) vlib_buffer_get_current(b0); \ + udp_hdr = (udp_header_t *) (ip_hdr + 1); \ + hicnb0 = hicn_get_buffer(b0); \ + \ + stats.pkts_interest_count += 1; \ + \ + inner_ip_hdr = (u8 *)(udp_hdr + 1); \ + u8 is_v6 = ((inner_ip_hdr[0] & 2) >> 1); \ + u8 is_icmp = is_v6*(inner_ip_hdr[7] == IPPROTO_ICMPV6) + \ + (1 - is_v6)*(inner_ip_hdr[10] == IPPROTO_ICMPV4); \ + \ + next0 = is_icmp*NEXT_MAPME_UDP##ipv + \ + (1-is_icmp)*NEXT_INTEREST_UDP##ipv; \ + \ + HICN_IFACE_UDP_ADD_LOCK_IP##ipv \ + (&(hicnb0->face_dpo_id), \ + &(ip_hdr->dst_address), \ + &(ip_hdr->src_address), \ + udp_hdr->dst_port, \ + udp_hdr->src_port, \ + GET_FACE_UDP##ipv \ + (), \ + &hicnb0->is_appface); \ + \ + vlib_buffer_advance(b0, sizeof(IP_HEADER_##ipv) + \ + sizeof(udp_header_t)); \ + \ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && \ + (b0->flags & VLIB_BUFFER_IS_TRACED))) \ + { \ + TRACE_INPUT_PKT_UDP##ipv *t = \ + vlib_add_trace (vm, node, b0, sizeof (*t)); \ + t->pkt_type = HICN_PKT_TYPE_INTEREST; \ + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; \ + t->next_index = next0; \ + } \ + \ + \ + /* Verify speculative enqueue, maybe switch current next frame */ \ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, \ + to_next, n_left_to_next, \ + bi0, next0); \ + }while(0) + + +#define iface_input_x2(ipv) \ + do { \ + vlib_buffer_t *b0, *b1; \ + u32 bi0, bi1; \ + u32 next0, next1 = ERROR_INPUT_UDP##ipv; \ + IP_HEADER_##ipv * ip_hdr0 = NULL, *ip_hdr1 = NULL; \ + u8 * inner_ip_hdr0 = NULL, *inner_ip_hdr1 = NULL; \ + udp_header_t * udp_hdr0 = NULL, *udp_hdr1 = NULL; \ + hicn_buffer_t * hicnb0, *hicnb1; \ + \ + /* Prefetch for next iteration. */ \ + { \ + vlib_buffer_t *b2, *b3; \ + b2 = vlib_get_buffer (vm, from[2]); \ + b3 = vlib_get_buffer (vm, from[3]); \ + CLIB_PREFETCH (b2, CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b3, CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b2->data, CLIB_CACHE_LINE_BYTES , LOAD); \ + CLIB_PREFETCH (b3->data, CLIB_CACHE_LINE_BYTES , LOAD); \ + } \ + \ + /* Dequeue a packet buffer */ \ + bi0 = from[0]; \ + bi1 = from[1]; \ + from += 2; \ + n_left_from -= 2; \ + to_next[0] = bi0; \ + to_next[1] = bi1; \ + to_next += 2; \ + n_left_to_next -= 2; \ + \ + b0 = vlib_get_buffer (vm, bi0); \ + b1 = vlib_get_buffer (vm, bi1); \ + ip_hdr0 = (IP_HEADER_##ipv *) vlib_buffer_get_current(b0); \ + ip_hdr1 = (IP_HEADER_##ipv *) vlib_buffer_get_current(b1); \ + udp_hdr0 = (udp_header_t *) (ip_hdr0 + 1); \ + udp_hdr1 = (udp_header_t *) (ip_hdr1 + 1); \ + hicnb0 = hicn_get_buffer(b0); \ + hicnb1 = hicn_get_buffer(b1); \ + \ + stats.pkts_interest_count += 2; \ + \ + inner_ip_hdr0 = (u8 *)(udp_hdr0 + 1); \ + inner_ip_hdr1 = (u8 *)(udp_hdr1 + 1); \ + u8 is_v6_0 = ((inner_ip_hdr0[0] & 2) >> 1); \ + u8 is_v6_1 = ((inner_ip_hdr1[0] & 2) >> 1); \ + u8 is_icmp0 = is_v6_0*(inner_ip_hdr0[7] == IPPROTO_ICMPV6) + \ + (1 - is_v6_0)*(inner_ip_hdr0[10] == IPPROTO_ICMPV4); \ + u8 is_icmp1 = is_v6_1*(inner_ip_hdr1[7] == IPPROTO_ICMPV6) + \ + (1 - is_v6_1)*(inner_ip_hdr1[10] == IPPROTO_ICMPV4); \ + \ + next0 = is_icmp0*NEXT_MAPME_UDP##ipv + \ + (1-is_icmp0)*NEXT_INTEREST_UDP##ipv; \ + next1 = is_icmp1*NEXT_MAPME_UDP##ipv + \ + (1-is_icmp1)*NEXT_INTEREST_UDP##ipv; \ + \ + HICN_IFACE_UDP_ADD_LOCK_IP##ipv \ + (&(hicnb0->face_dpo_id), \ + &(ip_hdr0->dst_address), \ + &(ip_hdr0->src_address), \ + udp_hdr0->dst_port, \ + udp_hdr0->src_port, \ + GET_FACE_UDP##ipv \ + (), \ + &hicnb0->is_appface); \ + \ + \ + HICN_IFACE_UDP_ADD_LOCK_IP##ipv \ + (&(hicnb1->face_dpo_id), \ + &(ip_hdr1->dst_address), \ + &(ip_hdr1->src_address), \ + udp_hdr1->dst_port, \ + udp_hdr1->src_port, \ + GET_FACE_UDP##ipv \ + (), \ + &hicnb1->is_appface); \ + \ + vlib_buffer_advance(b0, sizeof(IP_HEADER_##ipv) + \ + sizeof(udp_header_t)); \ + \ + vlib_buffer_advance(b1, sizeof(IP_HEADER_##ipv) + \ + sizeof(udp_header_t)); \ + \ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && \ + (b0->flags & VLIB_BUFFER_IS_TRACED))) \ + { \ + TRACE_INPUT_PKT_UDP##ipv *t = \ + vlib_add_trace (vm, node, b0, sizeof (*t)); \ + t->pkt_type = HICN_PKT_TYPE_INTEREST; \ + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; \ + t->next_index = next0; \ + } \ + \ + \ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && \ + (b1->flags & VLIB_BUFFER_IS_TRACED))) \ + { \ + TRACE_INPUT_PKT_UDP##ipv *t = \ + vlib_add_trace (vm, node, b1, sizeof (*t)); \ + t->pkt_type = HICN_PKT_TYPE_INTEREST; \ + t->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX]; \ + t->next_index = next1; \ + } \ + \ + \ + /* Verify speculative enqueue, maybe switch current next frame */ \ + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, \ + to_next, n_left_to_next, \ + bi0, bi1, next0, next1); \ + }while(0) + + +static uword +hicn_iface_udp4_input_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next, next_index; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + /* Dual loop, X2 */ + while (n_left_from >= 4 && n_left_to_next >= 2) + { + iface_input_x2 (4); + } + + /* Dual loop, X1 */ + while (n_left_from > 0 && n_left_to_next > 0) + { + iface_input_x1 (4); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, node->node_index, + HICNFWD_ERROR_INTERESTS, + stats.pkts_interest_count); + + return (frame->n_vectors); +} + +/* packet trace format function */ +static u8 * +hicn_iface_udp4_input_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_iface_udp4_input_trace_t *t = + va_arg (*args, hicn_iface_udp4_input_trace_t *); + + s = format (s, "IFACE_UDP4_INPUT: pkt: %d, sw_if_index %d, next index %d", + (int) t->pkt_type, t->sw_if_index, t->next_index); + return (s); +} + +/* + * Node registration for the interest forwarder node + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (hicn_iface_udp4_input_node) = + +{ + .function = hicn_iface_udp4_input_node_fn, + .name = "hicn-iface-udp4-input", + .vector_size = sizeof (u32), + .format_trace = hicn_iface_udp4_input_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (hicn_iface_udp4_input_error_strings), + .error_strings = hicn_iface_udp4_input_error_strings, + .n_next_nodes = HICN_IFACE_UDP4_INPUT_N_NEXT, + .next_nodes = + { + [HICN_IFACE_UDP4_INPUT_NEXT_INTEREST] = "hicn-interest-pcslookup", + [HICN_IFACE_UDP4_INPUT_NEXT_MAPME] = "hicn-mapme-ctrl", + [HICN_IFACE_UDP4_INPUT_NEXT_ERROR_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + + +static uword +hicn_iface_udp6_input_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next, next_index; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + /* Dual loop, X2 */ + while (n_left_from >= 4 && n_left_to_next >= 2) + { + iface_input_x2 (6); + } + + /* Dual loop, X1 */ + while (n_left_from > 0 && n_left_to_next > 0) + { + iface_input_x1 (6); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, node->node_index, + HICNFWD_ERROR_INTERESTS, + stats.pkts_interest_count); + + return (frame->n_vectors); +} + +/* packet trace format function */ +static u8 * +hicn_iface_udp6_input_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_iface_udp6_input_trace_t *t = + va_arg (*args, hicn_iface_udp6_input_trace_t *); + + s = format (s, "IFACE_UDP6_INPUT: pkt: %d, sw_if_index %d, next index %d", + (int) t->pkt_type, t->sw_if_index, t->next_index); + return (s); +} + +/* + * Node registration for the interest forwarder node + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (hicn_iface_udp6_input_node) = +{ + .function = hicn_iface_udp6_input_node_fn, + .name = "hicn-iface-udp6-input", + .vector_size = sizeof (u32), + .format_trace = hicn_iface_udp6_input_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (hicn_iface_udp6_input_error_strings), + .error_strings = hicn_iface_udp6_input_error_strings, + .n_next_nodes = HICN_IFACE_UDP6_INPUT_N_NEXT, + .next_nodes = + { + [HICN_IFACE_UDP6_INPUT_NEXT_INTEREST] = "hicn-interest-pcslookup", + [HICN_IFACE_UDP6_INPUT_NEXT_MAPME] = "hicn-mapme-ctrl", + [HICN_IFACE_UDP6_INPUT_NEXT_ERROR_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +/******* Iface Output *******/ + +always_inline void +hicn_iface_udp4_encap (vlib_main_t * vm, + vlib_buffer_t * b0, hicn_face_t * face) +{ + u16 new_l0 = 0; + ip4_header_t *ip0; + udp_header_t *udp0; + hicn_face_udp_t *face_udp = (hicn_face_udp_t *) face->data; + + /* Adjust vlib buffers */ + /* Set the right length on the header buffer */ + /* Move the next buffer current data pointer back to the ip+tcp header (hicn header) */ + int offset = sizeof (ip4_header_t) + sizeof (udp_header_t); + b0->current_data -= offset; + b0->current_length += offset; + + /* ip */ + ip0 = vlib_buffer_get_current (b0); + clib_memcpy (ip0, &(face_udp->hdrs.ip4.ip), sizeof (ip4_header_t) + + sizeof (udp_header_t)); + + /* Fix UDP length */ + udp0 = (udp_header_t *) (ip0 + 1); + + new_l0 = + clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0) - + sizeof (*ip0)); + udp0->length = new_l0; + + ip0->length = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)); + ip0->checksum = ip4_header_checksum (ip0); +} + +always_inline void +hicn_iface_udp6_encap (vlib_main_t * vm, + vlib_buffer_t * b0, hicn_face_t * face) +{ + int bogus0; + u16 new_l0; + ip6_header_t *ip0; + udp_header_t *udp0; + hicn_face_udp_t *face_udp = (hicn_face_udp_t *) face->data; + + /* Adjust vlib buffer */ + int offset = sizeof (ip6_header_t) + sizeof (udp_header_t); + b0->current_data -= offset; + b0->current_length += offset; + + /* ip */ + ip0 = vlib_buffer_get_current (b0); + clib_memcpy (ip0, &(face_udp->hdrs.ip6.ip), sizeof (ip6_header_t) + + sizeof (udp_header_t)); + + new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0) + - sizeof (*ip0)); + + ip0->payload_length = new_l0; + + /* Fix UDP length */ + udp0 = (udp_header_t *) (ip0 + 1); + udp0->length = new_l0; + + udp0->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip0, &bogus0); + + ASSERT (bogus0 == 0); + + if (udp0->checksum == 0) + udp0->checksum = 0xffff; +} + +static char *hicn_iface_udp4_output_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + +static char *hicn_iface_udp6_output_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; +} hicn_iface_udp4_output_trace_t; + +typedef enum +{ + HICN_IFACE_UDP4_OUTPUT_NEXT_LOOKUP, + HICN_IFACE_UDP4_OUTPUT_NEXT_ERROR_DROP, + HICN_IFACE_UDP4_OUTPUT_N_NEXT, +} hicn_iface_udp4_output_next_t; + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; +} hicn_iface_udp6_output_trace_t; + +typedef enum +{ + HICN_IFACE_UDP6_OUTPUT_NEXT_LOOKUP, + HICN_IFACE_UDP6_OUTPUT_NEXT_ERROR_DROP, + HICN_IFACE_UDP6_OUTPUT_N_NEXT, +} hicn_iface_udp6_output_next_t; + +#define ERROR_OUTPUT_UDP4 HICN_IFACE_UDP4_OUTPUT_NEXT_ERROR_DROP +#define ERROR_OUTPUT_UDP6 HICN_IFACE_UDP6_OUTPUT_NEXT_ERROR_DROP + +#define IP_HEADER_4 ip4_header_t +#define IP_HEADER_6 ip6_header_t + +#define NEXT_LOOKUP_UDP4 HICN_IFACE_UDP4_OUTPUT_NEXT_LOOKUP +#define NEXT_LOOKUP_UDP6 HICN_IFACE_UDP6_OUTPUT_NEXT_LOOKUP + +#define HICN_IFACE_UDP_ADD_LOCK_IP4 hicn_dpo_udp4_add_and_lock +#define HICN_IFACE_UDP_ADD_LOCK_IP6 hicn_dpo_udp6_add_and_lock + +#define HICN_FACE_UDP_ENCAP_IP4 hicn_iface_udp4_encap +#define HICN_FACE_UDP_ENCAP_IP6 hicn_iface_udp6_encap + +#define TRACE_OUTPUT_PKT_UDP4 hicn_iface_udp4_output_trace_t +#define TRACE_OUTPUT_PKT_UDP6 hicn_iface_udp6_output_trace_t + +#define iface_output_x1(ipv) \ + do { \ + vlib_buffer_t *b0; \ + u32 bi0; \ + u32 next0 = ERROR_OUTPUT_UDP##ipv; \ + hicn_face_t * face; \ + \ + /* Prefetch for next iteration. */ \ + if (n_left_from > 1) \ + { \ + vlib_buffer_t *b1; \ + b1 = vlib_get_buffer (vm, from[1]); \ + CLIB_PREFETCH (b1, CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b1->data, CLIB_CACHE_LINE_BYTES , LOAD); \ + } \ + /* Dequeue a packet buffer */ \ + bi0 = from[0]; \ + from += 1; \ + n_left_from -= 1; \ + to_next[0] = bi0; \ + to_next += 1; \ + n_left_to_next -= 1; \ + \ + b0 = vlib_get_buffer (vm, bi0); \ + \ + face = \ + hicn_dpoi_get_from_idx(vnet_buffer (b0)->ip.adj_index[VLIB_TX]); \ + \ + if (PREDICT_TRUE(face != NULL)) \ + { \ + HICN_FACE_UDP_ENCAP_IP##ipv \ + (vm, b0, face); \ + next0 = NEXT_LOOKUP_UDP##ipv; \ + stats.pkts_data_count += 1; \ + } \ + \ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && \ + (b0->flags & VLIB_BUFFER_IS_TRACED))) \ + { \ + TRACE_OUTPUT_PKT_UDP##ipv *t = \ + vlib_add_trace (vm, node, b0, sizeof (*t)); \ + t->pkt_type = HICN_PKT_TYPE_INTEREST; \ + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; \ + t->next_index = next0; \ + } \ + \ + \ + /* Verify speculative enqueue, maybe switch current next frame */ \ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, \ + to_next, n_left_to_next, \ + bi0, next0); \ + } while(0) + +#define iface_output_x2(ipv) \ + do { \ + vlib_buffer_t *b0, *b1; \ + u32 bi0, bi1; \ + u32 next0 = ERROR_OUTPUT_UDP##ipv, next1 = ERROR_OUTPUT_UDP##ipv; \ + hicn_face_t *face0, *face1; \ + \ + /* Prefetch for next iteration. */ \ + { \ + vlib_buffer_t *b2, *b3; \ + b2 = vlib_get_buffer (vm, from[2]); \ + b3 = vlib_get_buffer (vm, from[3]); \ + CLIB_PREFETCH (b2, CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b3, CLIB_CACHE_LINE_BYTES, STORE); \ + CLIB_PREFETCH (b2->data, CLIB_CACHE_LINE_BYTES , LOAD); \ + CLIB_PREFETCH (b3->data, CLIB_CACHE_LINE_BYTES , LOAD); \ + } \ + \ + /* Dequeue packets buffers */ \ + bi0 = from[0]; \ + bi1 = from[1]; \ + from += 2; \ + n_left_from -= 2; \ + to_next[0] = bi0; \ + to_next[1] = bi1; \ + to_next += 2; \ + n_left_to_next -= 2; \ + \ + b0 = vlib_get_buffer (vm, bi0); \ + b1 = vlib_get_buffer (vm, bi1); \ + \ + face0 = \ + hicn_dpoi_get_from_idx(vnet_buffer (b0)->ip.adj_index[VLIB_TX]); \ + face1 = \ + hicn_dpoi_get_from_idx(vnet_buffer (b0)->ip.adj_index[VLIB_TX]); \ + \ + if (PREDICT_TRUE(face0 != NULL)) \ + { \ + HICN_FACE_UDP_ENCAP_IP##ipv \ + (vm, b0, face0); \ + next0 = NEXT_LOOKUP_UDP##ipv; \ + stats.pkts_data_count += 1; \ + } \ + \ + if (PREDICT_TRUE(face1 != NULL)) \ + { \ + HICN_FACE_UDP_ENCAP_IP##ipv \ + (vm, b1, face1); \ + next0 = NEXT_LOOKUP_UDP##ipv; \ + stats.pkts_data_count += 1; \ + } \ + \ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && \ + (b0->flags & VLIB_BUFFER_IS_TRACED))) \ + { \ + TRACE_OUTPUT_PKT_UDP##ipv *t = \ + vlib_add_trace (vm, node, b0, sizeof (*t)); \ + t->pkt_type = HICN_PKT_TYPE_INTEREST; \ + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; \ + t->next_index = next0; \ + } \ + \ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && \ + (b1->flags & VLIB_BUFFER_IS_TRACED))) \ + { \ + TRACE_OUTPUT_PKT_UDP##ipv *t = \ + vlib_add_trace (vm, node, b1, sizeof (*t)); \ + t->pkt_type = HICN_PKT_TYPE_INTEREST; \ + t->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX]; \ + t->next_index = next1; \ + } \ + \ + \ + /* Verify speculative enqueue, maybe switch current next frame */ \ + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, \ + to_next, n_left_to_next, \ + bi0, bi1, next0, next1); \ + } while(0) + + +static uword +hicn_iface_udp4_output_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next, next_index; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from >= 4 && n_left_to_next >= 2) + { + iface_output_x2 (4); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + iface_output_x1 (4); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, node->node_index, + HICNFWD_ERROR_DATAS, stats.pkts_data_count); + + return (frame->n_vectors); +} + +/* packet trace format function */ +static u8 * +hicn_iface_udp4_output_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_iface_udp4_output_trace_t *t = + va_arg (*args, hicn_iface_udp4_output_trace_t *); + + s = format (s, "IFACE_UDP4_OUTPUT: pkt: %d, sw_if_index %d, next index %d", + (int) t->pkt_type, t->sw_if_index, t->next_index); + return (s); +} + + +/* + * Node registration for the interest forwarder node + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (hicn_iface_udp4_output_node) = +{ + .function = hicn_iface_udp4_output_node_fn, + .name = "hicn-iface-udp4-output", + .vector_size = sizeof (u32), + .format_trace = hicn_iface_udp4_output_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (hicn_iface_udp4_output_error_strings), + .error_strings = hicn_iface_udp4_output_error_strings, + .n_next_nodes = HICN_IFACE_UDP4_OUTPUT_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = + { + [HICN_IFACE_UDP4_OUTPUT_NEXT_LOOKUP] = "ip4-lookup", + [HICN_IFACE_UDP4_OUTPUT_NEXT_ERROR_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + + +static uword +hicn_iface_udp6_output_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next, next_index; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from >= 4 && n_left_to_next >= 2) + { + iface_output_x2 (6); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + iface_output_x1 (6); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, node->node_index, + HICNFWD_ERROR_DATAS, stats.pkts_data_count); + + return (frame->n_vectors); + +} + +/* packet trace format function */ +static u8 * +hicn_iface_udp6_output_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_iface_udp6_output_trace_t *t = + va_arg (*args, hicn_iface_udp6_output_trace_t *); + + s = format (s, "IFACE_UDP6_OUTPUT: pkt: %d, sw_if_index %d, next index %d", + (int) t->pkt_type, t->sw_if_index, t->next_index); + return (s); +} + +/* + * Node registration for the interest forwarder node + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (hicn_iface_udp6_output_node) = +{ + .function = hicn_iface_udp6_output_node_fn, + .name = "hicn-iface-udp6-output", + .vector_size = sizeof (u32), + .format_trace = hicn_iface_udp6_output_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (hicn_iface_udp6_output_error_strings), + .error_strings = hicn_iface_udp6_output_error_strings, + .n_next_nodes = HICN_IFACE_UDP6_OUTPUT_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = + { + [HICN_IFACE_UDP6_OUTPUT_NEXT_LOOKUP] = "ip6-lookup", + [HICN_IFACE_UDP6_OUTPUT_NEXT_ERROR_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/faces/udp/iface_udp_node.h b/hicn-plugin/src/faces/udp/iface_udp_node.h new file mode 100755 index 000000000..957d19217 --- /dev/null +++ b/hicn-plugin/src/faces/udp/iface_udp_node.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_IFACE_UDP_H__ +#define __HICN_IFACE_UDP_H__ + +#include + +extern vlib_node_registration_t hicn_iface_udp4_input_node; +extern vlib_node_registration_t hicn_iface_udp6_input_node; +extern vlib_node_registration_t hicn_iface_udp4_output_node; +extern vlib_node_registration_t hicn_iface_udp6_output_node; + +void hicn_iface_udp_init (vlib_main_t * vm); + +#endif // __HICN_FACE_UDP_H__ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/hashtb.c b/hicn-plugin/src/hashtb.c new file mode 100755 index 000000000..332da350d --- /dev/null +++ b/hicn-plugin/src/hashtb.c @@ -0,0 +1,1008 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include + +#include "pcs.h" +#include "hashtb.h" +#include "parser.h" +#include "error.h" + +/* return dvd/dvr, rounded up (intended for integer values) */ +#define CEIL(dvd, dvr) \ + ({ \ + __typeof__ (dvd) _dvd = (dvd); \ + __typeof__ (dvr) _dvr = (dvr); \ + (_dvd + _dvr - 1)/_dvr; \ + }) + +#ifndef ALIGN8 +#define ALIGN8(p) (((p) + 0x7) & ~(0x7)) +#endif + +#ifndef ALIGNPTR8 +#define ALIGNPTR8(p) ((void *)(((u8 * )(p) + 0x7) & ~(0x7))) +#endif + +#ifndef ALIGN64 +#define ALIGN64(p) (((p) + 0x3f) & ~(0x3f)) +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + + +/* + * Offset to aligned start of additional data (PIT/CS, FIB) embedded in each + * node. + */ +u32 ht_node_data_offset_aligned; + +/* Some support for posix vs vpp mem management */ +#define MEM_ALLOC(x) clib_mem_alloc_aligned((x), 8) +#define MEM_FREE(p) clib_mem_free((p)) + +/* + * Internal utilities + */ + +/* Allocate an overflow bucket */ +static hicn_hash_bucket_t * +alloc_overflow_bucket (hicn_hashtb_h h) +{ + hicn_hash_bucket_t *newbkt = NULL; + + if (h->ht_overflow_buckets_used < h->ht_overflow_bucket_count) + { + pool_get_aligned (h->ht_overflow_buckets, newbkt, 8); + + if (newbkt) + { + h->ht_overflow_buckets_used++; + } + } + return (newbkt); +} + +/* Free an overflow bucket; clear caller's pointer */ +static void +free_overflow_bucket (hicn_hashtb_h h, hicn_hash_bucket_t ** pb) +{ + hicn_hash_bucket_t *bkt = *pb; + + ASSERT (h->ht_overflow_buckets_used > 0); + + pool_put (h->ht_overflow_buckets, bkt); + h->ht_overflow_buckets_used--; + *pb = NULL; +} + +/* + * Init, allocate a new hashtable + */ +int +hicn_hashtb_alloc (hicn_hashtb_h * ph, u32 max_elems, size_t app_data_size) +{ + int ret = HICN_ERROR_NONE; + hicn_hashtb_h h = NULL; + u32 count; + u32 total_buckets; + size_t sz; + hicn_hash_node_t *nodep; + hicn_hash_bucket_t *bucket; + + if (ph == NULL) + { + ret = HICN_ERROR_HASHTB_INVAL; + goto done; + } + if (max_elems < HICN_HASHTB_MIN_ENTRIES || + max_elems > HICN_HASHTB_MAX_ENTRIES) + { + goto done; + } + /* Allocate and init main hashtable struct */ + h = MEM_ALLOC (sizeof (hicn_hashtb_t)); + if (h == NULL) + { + ret = HICN_ERROR_HASHTB_NOMEM; + goto done; + } + memset (h, 0, sizeof (hicn_hashtb_t)); + + /* Compute main table bucket (row) count and size, and allocate */ + + /* Consider the last entry as used for containing the overflow bucket */ + total_buckets = CEIL (max_elems, HICN_HASHTB_BUCKET_ENTRIES - 1); + count = ALIGN8 (CEIL (total_buckets, HICN_HASHTB_FILL_FACTOR)); + + h->ht_bucket_count = count; + + /* We _really_ expect to have buckets aligned on cache lines ... */ + sz = sizeof (hicn_hash_bucket_t); + assert (sz == ALIGN64 (sz)); + + h->ht_buckets = MEM_ALLOC (count * sz); + if (h->ht_buckets == NULL) + { + ret = HICN_ERROR_HASHTB_NOMEM; + goto done; + } + memset (h->ht_buckets, 0, count * sz); + + /* + * First time through, compute offset to aligned extra data start in + * each node struct it's crucial that both the node struct (that the + * base hashtable uses) and the extra data area (that's also probably + * a struct) are aligned. + */ + if (ht_node_data_offset_aligned == 0) + { + count = STRUCT_OFFSET_OF (hicn_hash_node_t, hn_data); + ht_node_data_offset_aligned = ALIGN8 (count); + } + //check app struct fits into space provided(HICN_HASH_NODE_APP_DATA_SIZE) + u32 ht_node_data_size; + ht_node_data_size = sizeof (hicn_hash_node_t) - ht_node_data_offset_aligned; + if (app_data_size > ht_node_data_size) + { + clib_error + ("hicn hashtable: fatal error: requested app data size(%u) > hashtb node's configured bytes available(%u), sizeof(hicn_shared_t)=%u, sizeof(hicn_pit_entry_t)=%u, sizeof(hicn_cs_entry_t)=%u", + app_data_size, ht_node_data_size, sizeof (hicn_pcs_shared_t), + sizeof (hicn_pit_entry_t), sizeof (hicn_cs_entry_t)); + } + /* + * Compute entry node count and size, allocate Allocate/'Hide' the + * zero-th node so can use zero as an 'empty' value + */ + pool_alloc_aligned (h->ht_nodes, max_elems, 8); + if (h->ht_nodes == NULL) + { + ret = HICN_ERROR_HASHTB_NOMEM; + goto done; + } + pool_get_aligned (h->ht_nodes, nodep, 8); + //alloc node 0 + nodep = nodep; /* Silence 'not used' warning */ + + h->ht_node_count = max_elems; + h->ht_nodes_used = 1; + + /* + * Compute overflow bucket count and size, allocate + */ + //count = ALIGN8(CEIL(max_elems, HICN_HASHTB_OVERFLOW_FRACTION)); + count = ALIGN8 (total_buckets - h->ht_bucket_count); + + pool_alloc_aligned (h->ht_overflow_buckets, count, 8); + if (h->ht_overflow_buckets == NULL) + { + ret = HICN_ERROR_HASHTB_NOMEM; + goto done; + } + /* 'Hide' the zero-th node so we can use zero as an 'empty' value */ + pool_get_aligned (h->ht_overflow_buckets, bucket, 8); + bucket = bucket; /* Silence 'not used' warning */ + + h->ht_overflow_bucket_count = count; + h->ht_overflow_buckets_used = 1; + +done: + + if (h) + { + if ((ret == HICN_ERROR_NONE) && ph) + { + *ph = h; + } + else + { + hicn_hashtb_free (&h); + } + } + return (ret); +} + +/* + * Free, de-allocate a hashtable + */ +int +hicn_hashtb_free (hicn_hashtb_h * ph) +{ + int ret = 0; + + if (ph) + { + if ((*ph)->ht_nodes) + { + pool_free ((*ph)->ht_nodes); + (*ph)->ht_nodes = 0; + } + if ((*ph)->ht_overflow_buckets) + { + pool_free ((*ph)->ht_overflow_buckets); + (*ph)->ht_overflow_buckets = 0; + } + if ((*ph)->ht_buckets) + { + MEM_FREE ((*ph)->ht_buckets); + (*ph)->ht_buckets = 0; + } + MEM_FREE (*ph); + + *ph = NULL; + } + return (ret); +} + + + +/* + * Basic api to lookup a specific hash+key tuple. This does the entire lookup + * operation, retrieving node structs and comparing keys, so it's not + * optimized for prefetching or high performance. + * + * Returns zero and mails back a node on success, errno otherwise. + */ +int +hicn_hashtb_lookup_node (hicn_hashtb_h h, const u8 * key, + u32 keylen, u64 hashval, u8 is_data, + u32 * node_id, u8 * dpo_ctx_id, u8 * vft_id, + u8 * is_cs, u8 * hash_entry_id, u32 * bucket_id, + u8 * bucket_is_overflow) +{ + return (hicn_hashtb_lookup_node_ex + (h, key, keylen, hashval, is_data, FALSE /* deleted nodes */ , + node_id, + dpo_ctx_id, vft_id, is_cs, hash_entry_id, bucket_id, + bucket_is_overflow)); +} + +/* + * Extended api to lookup a specific hash+key tuple. The implementation + * allows the caller to locate nodes that are marked for deletion, which is + * part of some hashtable applications, such as the FIB. + * + * This does the entire lookup operation, retrieving node structs and comparing + * keys, so it's not optimized for prefetching or high performance. + * + * Returns zero and mails back a node on success, errno otherwise. + */ +int +hicn_hashtb_lookup_node_ex (hicn_hashtb_h h, const u8 * key, + u32 keylen, u64 hashval, u8 is_data, + int include_deleted_p, u32 * node_id, + u8 * dpo_ctx_id, u8 * vft_id, u8 * is_cs, + u8 * hash_entry_id, u32 * bucket_id, + u8 * bucket_is_overflow) +{ + int i, ret = HICN_ERROR_HASHTB_HASH_NOT_FOUND; + int found_p = FALSE; + u32 bidx; + hicn_hash_bucket_t *bucket; + u32 current_bucket_id = ~0; + + /* + * Use some bits of the low half of the hash to locate a row/bucket + * in the table + */ + current_bucket_id = bidx = (hashval & (h->ht_bucket_count - 1)); + + bucket = h->ht_buckets + bidx; + + *bucket_is_overflow = 0; + /* Check the entries in the bucket for matching hash value */ + +loop_buckets: + + for (i = 0; i < HICN_HASHTB_BUCKET_ENTRIES && !found_p; i++) + { + /* + * If an entry is marked for deletion, ignore it unless the + * caller explicitly wants these nodes. + */ + if (bucket->hb_entries[i].he_flags & HICN_HASH_ENTRY_FLAG_DELETED) + { + if (!include_deleted_p) + { + continue; + } + } + if (bucket->hb_entries[i].he_msb64 == hashval) + { + /* + * Found a candidate - must retrieve the actual node + * and check the key. + */ + *node_id = bucket->hb_entries[i].he_node; + *dpo_ctx_id = bucket->hb_entries[i].dpo_ctx_id; + *vft_id = bucket->hb_entries[i].vft_id; + *is_cs = + bucket->hb_entries[i].he_flags & HICN_HASH_ENTRY_FLAG_CS_ENTRY; + *hash_entry_id = i; + *bucket_id = current_bucket_id; + /* + * If we are doing lookup for a data, do not take a + * lock in case of a hit with a CS entry + */ + if (!(is_data && *is_cs)) + { + bucket->hb_entries[i].locks++; + } + found_p = TRUE; + ret = HICN_ERROR_NONE; + goto done; + } + } + + /* + * Be prepared to continue to an overflow bucket if necessary. We + * only expect the last entry in a bucket to refer to an overflow + * bucket... + */ + i = HICN_HASHTB_BUCKET_ENTRIES - 1; + if (bucket->hb_entries[i].he_flags & HICN_HASH_ENTRY_FLAG_OVERFLOW) + { + current_bucket_id = bucket->hb_entries[i].he_node; + bucket = pool_elt_at_index (h->ht_overflow_buckets, + bucket->hb_entries[i].he_node); + *bucket_is_overflow = 1; + goto loop_buckets; + } +done: + + return (ret); +} + +/** + * This function allows to split the hash verification from the comparison of + * the entire key. Useful to exploit prefertching. + * return 1 if equals, 0 otherwise + */ +int +hicn_node_compare (const u8 * key, u32 keylen, hicn_hash_node_t * node) +{ + + int ret = 0; + + if (key && keylen == node->hn_keysize) + { + ret = (memcmp (key, node->hn_key.ks.key, keylen) == 0); + } + return ret; +} + +/* + * Utility to init a new entry in a hashtable bucket/row. We use this to add + * new a node+hash, and to clear out an entry during removal. + */ +void +hicn_hashtb_init_entry (hicn_hash_entry_t * entry, u32 nodeidx, + u64 hashval, u32 locks) +{ + entry->he_msb64 = hashval; + entry->he_node = nodeidx; + + /* Clear out some other fields in the entry */ + entry->he_flags = 0; + entry->locks = locks; +} + +/* + * Insert a node into the hashtable. We expect the caller has a) computed the + * hash value to use, b) initialized the node with the hash and key info, and + * c) filled in its app-specific data portion of the node. + */ + +int +hicn_hashtb_insert (hicn_hashtb_h h, hicn_hash_node_t * node, + hicn_hash_entry_t ** hash_entry, u64 hash, + u32 * node_id, + u8 * dpo_ctx_id, u8 * vft_id, u8 * is_cs, + u8 * hash_entry_id, u32 * bucket_id, + u8 * bucket_is_overflow) +{ + int i, ret = HICN_ERROR_HASHTB_INVAL; + u32 bidx; + hicn_hash_bucket_t *bucket, *newbkt; + int use_seven; + u32 current_bucket_id = ~0; + int is_overflow = 0; + + *hash_entry = NULL; + + if (h == NULL) + { + goto done; + } + /* + * Use some bits of the low half of the hash to locate a row/bucket + * in the table + */ + current_bucket_id = bidx = (hash & (h->ht_bucket_count - 1)); + + bucket = h->ht_buckets + bidx; + + use_seven = (h->ht_flags & HICN_HASHTB_FLAG_USE_SEVEN); + + /* Locate a free entry slot in the bucket */ + +loop_buckets: + + for (i = 0; i < HICN_HASHTB_BUCKET_ENTRIES; i++) + { + + /* + * If an entry is marked for deletion, ignore it + */ + if (bucket->hb_entries[i].he_flags & HICN_HASH_ENTRY_FLAG_DELETED) + { + continue; + } + /* + * Be sure that we are not inserting the same entry twice + */ + if (bucket->hb_entries[i].he_msb64 == hash) + { + /* + * We hit an existing pit entry. increase lock. + */ + + *node_id = bucket->hb_entries[i].he_node; + *dpo_ctx_id = bucket->hb_entries[i].dpo_ctx_id; + *vft_id = bucket->hb_entries[i].vft_id; + *is_cs = + bucket->hb_entries[i].he_flags & HICN_HASH_ENTRY_FLAG_CS_ENTRY; + *hash_entry_id = i; + *bucket_id = current_bucket_id; + *hash_entry = &(bucket->hb_entries[i]); + /* + * If we are doing lookup for a data, do not take a + * lock in case of a hit with a CS entry + */ + bucket->hb_entries[i].locks++; + *bucket_is_overflow = is_overflow; + ret = HICN_ERROR_HASHTB_EXIST; + goto done; + } + if ((bucket->hb_entries[i].he_msb64 == 0LL) && + (bucket->hb_entries[i].he_node == 0)) + { + /* Found a candidate -- fill it in */ + + /* + * Special case if the application asked not to use + * the last entry in each bucket. + */ + if ((i != (HICN_HASHTB_BUCKET_ENTRIES - 1)) || use_seven) + { + hicn_hashtb_init_entry (&(bucket->hb_entries[i]), + NODE_IDX_FROM_NODE (node, h), hash, 0); + + *hash_entry = &(bucket->hb_entries[i]); + + node->bucket_id = current_bucket_id; + node->entry_idx = i; + if (is_overflow) + node->hn_flags |= HICN_HASH_NODE_OVERFLOW_BUCKET; + + ret = HICN_ERROR_NONE; + goto done; + } + } + } + /* + * Be prepared to continue to an overflow bucket if necessary, or to + * add a new overflow bucket. We only expect the last entry in a + * bucket to refer to an overflow bucket... + */ + i = HICN_HASHTB_BUCKET_ENTRIES - 1; + if (bucket->hb_entries[i].he_flags & HICN_HASH_ENTRY_FLAG_OVERFLOW) + { + /* Existing overflow bucket - re-start the search loop */ + current_bucket_id = bucket->hb_entries[i].he_node; + bucket = pool_elt_at_index (h->ht_overflow_buckets, current_bucket_id); + is_overflow = 1; + goto loop_buckets; + + } + else + { + /* + * Overflow - reached the end of a bucket without finding a + * free entry slot. Need to allocate an overflow bucket, and + * connect it to this bucket. + */ + newbkt = alloc_overflow_bucket (h); + if (newbkt == NULL) + { + ret = HICN_ERROR_HASHTB_NOMEM; + goto done; + } + /* + * We're touching some more bytes than we absolutely have to + * here, but ... that seems ok. + */ + memset (newbkt, 0, sizeof (hicn_hash_bucket_t)); + + if (use_seven) + { + /* + * Copy existing entry into new bucket - we really + * expect these to be properly aligned so they can be + * treated as int. + */ + memcpy (&(newbkt->hb_entries[0]), + &(bucket->hb_entries[i]), sizeof (hicn_hash_entry_t)); + + /* Update bucket id and entry_idx on the hash node */ + hicn_hash_node_t *node = + pool_elt_at_index (h->ht_nodes, newbkt->hb_entries[0].he_node); + node->bucket_id = (newbkt - h->ht_overflow_buckets); + node->entry_idx = 0; + node->hn_flags |= HICN_HASH_NODE_OVERFLOW_BUCKET; + + } + /* + * Connect original bucket to the index of the new overflow + * bucket + */ + bucket->hb_entries[i].he_flags |= HICN_HASH_ENTRY_FLAG_OVERFLOW; + bucket->hb_entries[i].he_node = (newbkt - h->ht_overflow_buckets); + + /* Add new entry to new overflow bucket */ + bucket = newbkt; + + /* + * Use entry [1] in the new bucket _if_ we just copied into + * entry [zero] above. + */ + if (use_seven) + { + + hicn_hashtb_init_entry (&(bucket->hb_entries[1]), + NODE_IDX_FROM_NODE (node, h), hash, 0); + *hash_entry = &(bucket->hb_entries[1]); + + node->bucket_id = (newbkt - h->ht_overflow_buckets); + node->entry_idx = 1; + node->hn_flags |= HICN_HASH_NODE_OVERFLOW_BUCKET; + } + else + { + + hicn_hashtb_init_entry (&(bucket->hb_entries[0]), + NODE_IDX_FROM_NODE (node, h), hash, 0); + *hash_entry = &(bucket->hb_entries[0]); + node->bucket_id = (newbkt - h->ht_overflow_buckets); + node->entry_idx = 0; + node->hn_flags |= HICN_HASH_NODE_OVERFLOW_BUCKET; + } + } + + /* And we're done with the overflow bucket */ + ret = HICN_ERROR_NONE; + +done: + + return (ret); +} + +/* + * Delete a node from a hashtable using the node itself, and delete/free the + * node. Caller's pointer is cleared on success. + */ +void +hicn_hashtb_delete (hicn_hashtb_h h, hicn_hash_node_t ** pnode, u64 hashval) +{ + + hicn_hashtb_remove_node (h, *pnode, hashval); + hicn_hashtb_free_node (h, *pnode); + *pnode = NULL; + +} + +/* + * Delete an entry from a hashtable using the node itself. If the node was + * stored in an overflow bucket, and the bucket is empty after freeing the + * node, the bucket is freed as well. + */ +void +hicn_hashtb_remove_node (hicn_hashtb_h h, hicn_hash_node_t * node, + u64 hashval) +{ + int i, count; + u32 bidx, overflow_p; + hicn_hash_bucket_t *bucket, *parent; + + if ((h == NULL) || (node == NULL)) + { + goto done; + } + if (node->hn_flags & HICN_HASH_NODE_OVERFLOW_BUCKET) + bucket = pool_elt_at_index (h->ht_overflow_buckets, node->bucket_id); + else + { + /* + * Use some bits of the low half of the hash to locate a + * row/bucket in the table + */ + bidx = (hashval & (h->ht_bucket_count - 1)); + ASSERT (bidx == node->bucket_id); + bucket = h->ht_buckets + node->bucket_id; + } + + overflow_p = node->hn_flags & HICN_HASH_NODE_OVERFLOW_BUCKET; + + /* Clear out the entry. */ + hicn_hashtb_init_entry (&(bucket->hb_entries[node->entry_idx]), 0, 0LL, 0); + + if (!overflow_p) + { + /* + * And we're done, in the easy case where we didn't change an + * overflow bucket + */ + goto done; + } + /* + * The special case: if this is the last remaining entry in an + * overflow bucket, liberate the bucket. That in turn has a special + * case if this bucket is in the middle of a chain of overflow + * buckets. + * + * Note that we're not trying aggressively (yet) to condense buckets at + * every possible opportunity. + */ + + /* + * Reset this flag; we'll set it again if this bucket links to + * another + */ + overflow_p = FALSE; + + for (i = 0, count = 0; i < HICN_HASHTB_BUCKET_ENTRIES; i++) + { + if (bucket->hb_entries[i].he_node != 0) + { + count++; + } + if (i == (HICN_HASHTB_BUCKET_ENTRIES - 1) && + (bucket->hb_entries[i].he_flags & HICN_HASH_ENTRY_FLAG_OVERFLOW)) + { + count--; /* Doesn't count as a 'real' entry */ + overflow_p = TRUE; + } + } + + if (count > 0) + { + /* Still a (real) entry in the row */ + goto done; + } + /* + * Need to locate the predecessor of 'bucket': start at the beginning + * of the chain of buckets and move forward + */ + bidx = (hashval & (h->ht_bucket_count - 1)); + + for (parent = h->ht_buckets + bidx; parent != NULL;) + { + + if ((parent->hb_entries[(HICN_HASHTB_BUCKET_ENTRIES - 1)].he_flags & + HICN_HASH_ENTRY_FLAG_OVERFLOW) == 0) + { + parent = NULL; + break; + } + bidx = parent->hb_entries[(HICN_HASHTB_BUCKET_ENTRIES - 1)].he_node; + + if (pool_elt_at_index (h->ht_overflow_buckets, bidx) == bucket) + { + /* + * Found the predecessor of 'bucket'. If 'bucket' has + * a successor, connect 'parent' to it, and take + * 'bucket out of the middle. + */ + if (overflow_p) + { + parent->hb_entries[(HICN_HASHTB_BUCKET_ENTRIES - 1)].he_node = + bucket->hb_entries[(HICN_HASHTB_BUCKET_ENTRIES - 1)].he_node; + } + else + { + /* + * Just clear the predecessor entry pointing + * at 'bucket' + */ + hicn_hashtb_init_entry (&parent->hb_entries + [(HICN_HASHTB_BUCKET_ENTRIES - 1)], 0, + 0LL, 0); + } + + break; + } + /* + * After the first iteration, 'parent' will be an overflow + * bucket too + */ + parent = pool_elt_at_index (h->ht_overflow_buckets, bidx); + } + + /* We really expect to have found the predecessor */ + ASSERT (parent != NULL); + + /* And now, finally, we can put 'bucket' back on the free list */ + free_overflow_bucket (h, &bucket); + +done: + return; +} + +/* + * Prepare a hashtable node, supplying the key, and computed hash info. + */ +void +hicn_hashtb_init_node (hicn_hashtb_h h, hicn_hash_node_t * node, + const u8 * key, u32 keylen) +{ + assert (h != NULL); + assert (node != NULL); + assert (keylen <= HICN_PARAM_HICN_NAME_LEN_MAX); + + /* Init the node struct */ + node->hn_flags = HICN_HASH_NODE_FLAGS_DEFAULT; + node->hn_keysize = 0; + node->hn_keysize = keylen; + memcpy (node->hn_key.ks.key, key, keylen); + node->bucket_id = ~0; + node->entry_idx = ~0; +} + +/* + * Release a hashtable node back to the free list when an entry is cleared + */ +void +hicn_hashtb_free_node (hicn_hashtb_h h, hicn_hash_node_t * node) +{ + ASSERT (h->ht_nodes_used > 0); + + /* Return 'node' to the free list */ + pool_put (h->ht_nodes, node); + h->ht_nodes_used--; + +} + +/* + * Walk a hashtable, iterating through the nodes, keeping context in 'ctx'. + */ +int +hicn_hashtb_next_node (hicn_hashtb_h h, hicn_hash_node_t ** pnode, u64 * ctx) +{ + int i, j, ret = HICN_ERROR_HASHTB_INVAL; + u32 bidx, entry; + hicn_hash_bucket_t *bucket; + + if ((h == NULL) || (pnode == NULL) || (ctx == NULL)) + { + goto done; + } + /* Special-case for new iteration */ + if (*ctx == HICN_HASH_WALK_CTX_INITIAL) + { + bidx = 0; + bucket = &h->ht_buckets[0]; + entry = 0; + j = 0; + i = 0; + goto search_table; + } + /* Convert context to bucket and entry indices */ + bidx = *ctx & 0xffffffffLL; + entry = *ctx >> 32; + + if (bidx >= h->ht_bucket_count) + { + ret = HICN_ERROR_HASHTB_HASH_NOT_FOUND; + goto done; + } + bucket = h->ht_buckets + bidx; + + /* Init total index into entries (includes fixed bucket and overflow) */ + j = 0; + +skip_processed_bucket_chunks: + /* + * Figure out where to resume the search for the next entry in the + * table, by trying to find the last entry returned, from the cookie. + * Loop walks one (regular or overflow) bucket chunk, label is used + * for walking chain of chunks. Note that if there was a deletion or + * an addition that created an overflow, iterator can skip entries or + * return duplicate entries, for entries that are present from before + * the walk starts until after it ends. + */ + + for (i = 0; i < HICN_HASHTB_BUCKET_ENTRIES; i++, j++) + { + if (j > entry) + { + /* + * Start search for next here, use existing 'bucket' + * and 'i' + */ + break; + } + /* + * If an entry is marked for deletion, ignore it + */ + if (bucket->hb_entries[i].he_flags & HICN_HASH_ENTRY_FLAG_DELETED) + { + continue; + } + /* + * Be prepared to continue to an overflow bucket if + * necessary. (We only expect the last entry in a bucket to + * refer to an overflow bucket...) + */ + if (i == (HICN_HASHTB_BUCKET_ENTRIES - 1)) + { + if (bucket->hb_entries[i].he_flags & HICN_HASH_ENTRY_FLAG_OVERFLOW) + { + bucket = pool_elt_at_index (h->ht_overflow_buckets, + bucket->hb_entries[i].he_node); + + /* Increment overall entry counter 'j' */ + j++; + + goto skip_processed_bucket_chunks; + } + /* + * end of row (end of fixed bucket plus any + * overflows) + */ + i = 0; + j = 0; + + bidx++; + + /* Special case - we're at the end */ + if (bidx >= h->ht_bucket_count) + { + ret = HICN_ERROR_HASHTB_HASH_NOT_FOUND; + goto done; + } + bucket = h->ht_buckets + bidx; + break; + } + } + +search_table: + + /* + * Now we're searching through the table for the next entry that's + * set + */ + + for (; i < HICN_HASHTB_BUCKET_ENTRIES; i++, j++) + { + /* + * If an entry is marked for deletion, ignore it + */ + if (bucket->hb_entries[i].he_flags & HICN_HASH_ENTRY_FLAG_DELETED) + { + continue; + } + /* Is this entry set? */ + if (bucket->hb_entries[i].he_node != 0) + { + + /* Retrieve the node struct */ + *pnode = pool_elt_at_index (h->ht_nodes, + bucket->hb_entries[i].he_node); + + /* + * Set 'entry' as we exit, so we can update the + * cookie + */ + entry = j; + ret = HICN_ERROR_NONE; + break; + } + /* + * Be prepared to continue to an overflow bucket if + * necessary. (We only expect the last entry in a bucket to + * refer to an overflow bucket...) + */ + if (i == (HICN_HASHTB_BUCKET_ENTRIES - 1)) + { + if (bucket->hb_entries[i].he_flags & HICN_HASH_ENTRY_FLAG_OVERFLOW) + { + bucket = pool_elt_at_index (h->ht_overflow_buckets, + bucket->hb_entries[i].he_node); + /* + * Reset per-bucket index 'i', here (not done + * in iterator) + */ + i = 0; + /* Increment overall entry counter 'j' */ + j++; + + goto search_table; + } + else + { + /* + * Move to next bucket, resetting per-bucket + * and overall entry indexes + */ + i = 0; + j = 0; + + bidx++; + + /* Special case - we're at the end */ + if (bidx >= h->ht_bucket_count) + { + ret = HICN_ERROR_HASHTB_HASH_NOT_FOUND; + goto done; + } + bucket = h->ht_buckets + bidx; + goto search_table; + } + } + } + +done: + + if (ret == HICN_ERROR_NONE) + { + /* Update context */ + *ctx = bidx; + *ctx |= ((u64) entry << 32); + } + return (ret); +} + +int +hicn_hashtb_key_to_buf (u8 ** vec_res, hicn_hashtb_h h, + const hicn_hash_node_t * node) +{ + int ret = HICN_ERROR_NONE; + u8 *vec = *vec_res; + + if (node->hn_keysize <= HICN_HASH_KEY_BYTES) + { + vec_add (vec, node->hn_key.ks.key, node->hn_keysize); + } + *vec_res = vec; + return (ret); +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/hashtb.h b/hicn-plugin/src/hashtb.h new file mode 100755 index 000000000..1690419a1 --- /dev/null +++ b/hicn-plugin/src/hashtb.h @@ -0,0 +1,550 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_HASHTB_H__ +#define __HICN_HASHTB_H__ + +#include +#include +#include + +#include "params.h" +#include "parser.h" +#include "error.h" + +/* Handy abbreviations for success status, and for boolean values */ +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +/* + * Lookup is finding a hashtable record whose name matches the name being + * looked up. Most of the lookup work is based on the hash value of the two + * names. Note that the intel cache line size is 64 bytes, and some platforms + * load in 2 cache lines together. - first step is to match a record at the + * bucket/slot level (htab has an array of htbucket_t/htbc_elmt, where each + * bucket has 7 slots to hold indices for entries.) Matching at this level + * implies - the hashes of the lookup name and the record map to the same + * bucket - the high 32 bits of the hashes (slot bce_hash_msb32s) match. Read + * cost (on the hash table size, i.e. ignoring reading the name being looked + * up): - First step normally requires 1 cache line load to pull in the + * 64-byte htbucket_t with the 7 element slot table holding the hash_msb32s. + * - In the event (hopefully rare for a hash table with appropriate number of + * buckets) that more than 7 elements hash to the same bucket, lookup may + * well need to look not only at the static htbc_elmt_t but at the chain of + * dynamically allocated htbc_elmt_t's linked to the static htbc_elmt_t, + * where each of these holds slot entries for additional elements. - Before + * reaching that point, it is initially required is to read in the hash table + * record fields (ht_bucket_buf, htnode buf, etc) holding pointers to the + * arrays, but these cache lines are common to all lookups so will likely + * already be in the cache. - second step is to match at the record level + * (htnode/htkb level) once a slot-level match happens. Matching at this + * level implies the following match - the hash values (the full 64 bits vs. + * bucket+32 msb, above) With siphash, two names hashing to the same 64-bit + * value is quite rare. - the name which, on the hash table side, is stored + * as a list of htkb_t (key buffers). [In some cases, the full name is not + * compared, and a match is assumed based on hash value match. Read cost: - + * htnode_t, in one cache line, holds hash value and index for the htkb at + * the head of the key buffer list - each key buffer (htkb_t) is cache line + * aligned/sized, and holds 60 bytes of the name and requires a cache line + * read. Simplification is that a fib lookup requires 3 cache lines: - bucket + * - htnode - single key buffer (for cases where a name comparision is done) + * + * Some hashtables (for which rare false positives are tolerable) store hash + * values but no keys. (In ISM NDN forwarder, this was used for dcm_dpf: data + * cache manager's dataplane filter, where speed was critical and very rare + * false positives would be detected in the full dcm check.) - No key buffers + * are used (or even allocated at hash table creation). + */ + +#define HICN_HASH_INVALID_IDX ~0 +/* + * for hicn_hashtb_next_node() iterator, this otherwise illegal context value + * indicates first call of iteration. Note: must not be 0, which is a legal + * context value. + */ +#define HICN_HASH_WALK_CTX_INITIAL (~((u64)0)) + +/* + * Key memory allocation scheme. + * + * The key is the bytestring that a hashtable entry is storing, e.g. a fib + * prefix or packet name. The hash of the name is used not just to pick the + * bucket, but also as a surrogate for the actual key value. + * + * Client calls pass key/name as contiguous memory for lookup/add/delete but + * hashable stores its copy of the key/name as a list of one or more hash_key + * structs. - key memory is managed as a list of keys (cache line + * sized/aligned buffers). - If (keysize < 128) then use key struct's full + * 128 bytes - If not, first key struct is head of a linked list of elements + * where the first bytes are used for the key and the last 4 bytes are the + * index of the next entry (or an end marker). - key memory is generally the + * single largest use of memory in the hash table, especially for PIT, as + * names are bigger than node structs (which is also per name/entry). + * + */ + +/* Compute hash node index from node pointer */ +#define NODE_IDX_FROM_NODE(p, h) \ + (u32)((p) - ((h)->ht_nodes)) + +#define HICN_HASH_KEY_BYTES 20 + +typedef struct +{ + struct + { + u8 key[HICN_HASH_KEY_BYTES]; + } ks; /* Entire key in one block */ +} hicn_hash_key_t; + +/* + * Ratio of extra key blocks to allocate, in case the embedded ones aren't + * sufficient. This is the fraction of the number of entries allocated. + */ +#define HICN_HASHTB_KEY_RATIO 8 + +/* + * hash node, used to store a hash table entry; indexed by an entry in a + * bucket. the node contains an embedded key; long keys are stored as chains + * of keys. + * + * The memory block for a node includes space for client data, additional memory + * located off the end of the htnode data structure. Size of client-supplied + * data is fixed, so we can use vpp pools. The PIT and FIB need to ensure + * that they fit within the available data area, or change the size to + * accomodate their needs. + * + * NOTE: app_data_size currently applies to all apps, i.e. bigger FIB nodes + * means (leads to, requires) bigger PCS nodes + */ + +/* Size this so that we can offer 64B aligned on 64-bits to the applications */ +/* New PIT entry syze 62B */ +#define HICN_HASH_NODE_APP_DATA_SIZE 4184 //to support 512 entry //96 //190 to support 50 faces + +/* How to align in the right way */ +typedef struct __attribute__ ((packed)) hicn_hash_node_s +{ + /* Bucket id containing the corresponding hash entry. */ + u32 bucket_id; + + /* Hash entry index in the bucket */ + u32 entry_idx; + + /* Total size of the key */ + u16 hn_keysize; + + /* 1 byte of flags for application use */ + u8 hn_flags; + + u8 _hn_reserved1; /* TBD, to align what follows back to + * 32 */ + + hicn_hash_key_t hn_key; /* Key value embedded in the node, may chain + * to more key buffers if necessary */ + + /* 32B + HICN_HASH_NODE_APP_DATA_SIZE */ + /* Followed by app-specific data (fib or pit or cs entry, e.g.) */ + u8 hn_data[HICN_HASH_NODE_APP_DATA_SIZE]; + +} hicn_hash_node_t; + +#define HICN_HASH_NODE_FLAGS_DEFAULT 0x00 +#define HICN_HASH_NODE_CS_FLAGS 0x01 +#define HICN_HASH_NODE_OVERFLOW_BUCKET 0x02 + +/* + * hicn_hash_entry_t Structure holding all or part of a hash value, a node + * index, and other key pieces of info. + * + * - 128 bytes/bucket with 19 bytes/entry gives 6 entries, or 5 entries plus + * next bucket ptr if overflow Changes in this structure will affect + * hicn_hash_bucket_t + */ +typedef struct __attribute__ ((packed)) hicn_hash_entry_s +{ + + /* MSB of the hash value */ + u64 he_msb64; + + /* Index of node block */ + u32 he_node; + + /* + * Lock to prevent hash_node deletion while there are still interest + * or data referring to it + */ + u32 locks; + + /* A few flags, including 'this points to a chain of buckets' */ + u8 he_flags; + + /* + * Index of the virtual function table corresponding to the dpo_ctx + * strategy + */ + u8 vft_id; + + /* Index of dpo */ + u8 dpo_ctx_id; + +} hicn_hash_entry_t; + +#define HICN_HASH_ENTRY_FLAGS_DEFAULT 0x00 + +/* If entry is PIT this flag is 0 */ +#define HICN_HASH_ENTRY_FLAG_CS_ENTRY 0x01 + +/* + * This entry heads a chain of overflow buckets (we expect to see this only + * in the last entry in a bucket.) In this case, the index is to an overflow + * bucket rather than to a single node block. + */ +#define HICN_HASH_ENTRY_FLAG_OVERFLOW 0x04 + +/* This entry has been marked for deletion */ +#define HICN_HASH_ENTRY_FLAG_DELETED 0x08 + +/* Use fast he_timeout units for expiration, slow if not */ +#define HICN_HASH_ENTRY_FLAG_FAST_TIMEOUT 0x10 + +/* + * hash bucket: Contains an array of entries. Cache line sized/aligned, so no + * room for extra fields unless bucket size is increased to 2 cache lines or + * the entry struct shrinks. + */ + +/* + * Overflow bucket ratio as a fraction of the fixed/configured count; a pool + * of hash buckets used if a row in the fixed table overflows. + */ +#define HICN_HASHTB_BUCKET_ENTRIES 6 + +typedef struct __attribute__ ((packed)) +{ + hicn_hash_entry_t hb_entries[HICN_HASHTB_BUCKET_ENTRIES]; + u64 align1; + u32 align2; + u16 align3; +} hicn_hash_bucket_t; + +/* Overall target fill-factor for the hashtable */ +#define HICN_HASHTB_FILL_FACTOR 4 + +#define HICN_HASHTB_MIN_ENTRIES (1 << 4) // includes dummy node 0 entry +#define HICN_HASHTB_MAX_ENTRIES (1 << 24) + +#define HICN_HASHTB_MIN_BUCKETS (1 << 10) + +/* + * htab_t + * + * Hash table main structure. + * + * Contains - pointers to dynamically allocated arrays of cache-line + * sized/aligned structures (buckets, nodes, keys). Put frequently accessed + * fields in the first cache line. + */ +typedef struct hicn_hashtb_s +{ + + /* 8B - main array of hash buckets */ + hicn_hash_bucket_t *ht_buckets; + + /* 8B - just-in-case block of overflow buckets */ + hicn_hash_bucket_t *ht_overflow_buckets; + + /* 8B - block of nodes associated with entries in buckets */ + hicn_hash_node_t *ht_nodes; + + /* Flags */ + u32 ht_flags; + + /* Count of buckets allocated in the main array */ + u32 ht_bucket_count; + + /* Count of overflow buckets allocated */ + u32 ht_overflow_bucket_count; + u32 ht_overflow_buckets_used; + + /* Count of nodes allocated */ + u32 ht_node_count; + u32 ht_nodes_used; + + /* Count of overflow key structs allocated */ + u32 ht_key_count; + u32 ht_keys_used; + +} hicn_hashtb_t, *hicn_hashtb_h; + +/* + * Offset to aligned start of additional data (PIT/CS, FIB) embedded in each + * node. + */ +extern u32 ht_node_data_offset_aligned; + +/* Flags for hashtable */ + +#define HICN_HASHTB_FLAGS_DEFAULT 0x00 + +/* + * Don't use the last entry in each bucket - only use it for overflow. We use + * this for the FIB, currently, so that we can support in-place FIB changes + * that would be difficult if there were hash entry copies as part of + * overflow handling. + */ +#define HICN_HASHTB_FLAG_USE_SEVEN 0x04 +#define HICN_HASHTB_FLAG_KEY_FMT_PFX 0x08 +#define HICN_HASHTB_FLAG_KEY_FMT_NAME 0x10 + +/* + * Max prefix name components we'll support in our incremental hashing; + * currently used only for LPM in the FIB. + */ +#define HICN_HASHTB_MAX_NAME_COMPS HICN_PARAM_FIB_ENTRY_PFX_COMPS_MAX + +/* + * APIs and inlines + */ + +/* Compute hash node index from node pointer */ +static inline u32 +hicn_hashtb_node_idx_from_node (hicn_hashtb_h h, hicn_hash_node_t * p) +{ + return (p - h->ht_nodes); +} + +/* Retrieve a hashtable node by node index */ +static inline hicn_hash_node_t * +hicn_hashtb_node_from_idx (hicn_hashtb_h h, u32 idx) +{ + return (pool_elt_at_index (h->ht_nodes, idx)); +} + +/* Allocate a brand-new hashtable */ +int +hicn_hashtb_alloc (hicn_hashtb_h * ph, u32 max_elems, size_t app_data_size); + +/* Free a hashtable, including its embedded arrays */ +int hicn_hashtb_free (hicn_hashtb_h * ph); + +/* Hash a bytestring, currently using bihash */ +u64 hicn_hashtb_hash_bytestring (const u8 * key, u32 keylen); + +always_inline hicn_hash_entry_t * +hicn_hashtb_get_entry (hicn_hashtb_h h, u32 entry_idx, u32 bucket_id, + u8 bucket_overflow) +{ + hicn_hash_bucket_t *bucket; + if (bucket_overflow) + bucket = pool_elt_at_index (h->ht_overflow_buckets, bucket_id); + else + bucket = (hicn_hash_bucket_t *) (h->ht_buckets + bucket_id); + + return &(bucket->hb_entries[entry_idx]); +} + +/* Hash a name, currently using bihash */ +always_inline u64 +hicn_hashtb_hash_name (const u8 * key, u16 keylen) +{ + if (key != NULL && keylen == HICN_V4_NAME_LEN) + { + clib_bihash_kv_8_8_t kv; + kv.key = ((u64 *) key)[0]; + return clib_bihash_hash_8_8 (&kv); + } + else if (key != NULL && keylen == HICN_V6_NAME_LEN) + { + clib_bihash_kv_24_8_t kv; + kv.key[0] = ((u64 *) key)[0]; + kv.key[1] = ((u64 *) key)[1]; + kv.key[2] = ((u32 *) key)[4]; + return clib_bihash_hash_24_8 (&kv); + } + else + { + return (-1LL); + } +} + + +/* + * Prepare a hashtable node for insertion, supplying the key and computed + * hash info. This sets up the node->key relationship, possibly allocating + * overflow key buffers. + */ +void +hicn_hashtb_init_node (hicn_hashtb_h h, hicn_hash_node_t * node, + const u8 * key, u32 keylen); + +/* + * Insert a node into the hashtable. We expect the caller has used the init + * api to set the node key and hash info, and populated the extra data area + * (if any) - or done the equivalent work itself. + */ +int +hicn_hashtb_insert (hicn_hashtb_h h, hicn_hash_node_t * node, + hicn_hash_entry_t ** hash_entry, u64 hash, + u32 * node_id, + u8 * dpo_ctx_id, u8 * vft_id, u8 * is_cs, + u8 * hash_entry_id, u32 * bucket_id, + u8 * bucket_is_overflow); + +/* + * Basic api to lookup a specific hash+key tuple. This does the entire lookup + * operation, retrieving node structs and comparing keys, so it's not + * optimized for prefetching or high performance. + * + * Returns zero and mails back a node on success, errno otherwise. + */ +int +hicn_hashtb_lookup_node (hicn_hashtb_h h, const u8 * key, + u32 keylen, u64 hashval, u8 is_data, + u32 * node_id, u8 * dpo_ctx_id, u8 * vft_id, + u8 * is_cs, u8 * hash_entry_id, u32 * bucket_id, + u8 * bucket_is_overflow); + +/* + * Extended api to lookup a specific hash+key tuple. The implementation + * allows the caller to locate nodes that are marked for deletion; this is + * part of some hashtable applications, such as the FIB. + * + * This does the entire lookup operation, retrieving node structs and comparing + * keys, so it's not optimized for prefetching or high performance. + * + * Returns zero and mails back a node on success, errno otherwise. + */ +int +hicn_hashtb_lookup_node_ex (hicn_hashtb_h h, const u8 * key, + u32 keylen, u64 hashval, u8 is_data, + int include_deleted_p, u32 * node_id, + u8 * dpo_ctx_id, u8 * vft_id, u8 * is_cs, + u8 * hash_entry_id, u32 * bucket_id, + u8 * bucket_is_overflow); + +/** + * @brief Compares the key in the node with the given key + * + * This function allows to split the hash verification from the comparison of + * the entire key. Useful to exploit prefertching. + * @result 1 if equals, 0 otherwise + */ +int hicn_node_compare (const u8 * key, u32 keylen, hicn_hash_node_t * node); + +/* + * Remove a node from a hashtable using the node itself. The internal data + * structs are cleaned up, but the node struct itself is not: the caller must + * free the node itself. + */ +void hicn_hashtb_remove_node (hicn_hashtb_h h, hicn_hash_node_t * node, + u64 hashval); + +/* + * Delete a node from a hashtable using the node itself, and delete/free the + * node. Caller's pointer is cleared on success. + */ +void hicn_hashtb_delete (hicn_hashtb_h h, hicn_hash_node_t ** pnode, + u64 hashval); + +/* + * Utility to init a new entry in a hashtable bucket/row. We use this to add + * new a node+hash, and to clear out an entry during removal. + */ +void +hicn_hashtb_init_entry (hicn_hash_entry_t * entry, + u32 nodeidx, u64 hashval, u32 locks); + + +/* + * Return data area embedded in a hash node struct. We maintain an 'offset' + * value in case the common node body struct doesn't leave the data area + * aligned properly. + */ +static inline void * +hicn_hashtb_node_data (hicn_hash_node_t * node) +{ + return ((u8 *) (node) + ht_node_data_offset_aligned); +} + +/* + * Use some bits of the low half of the hash to locate a row/bucket in the + * table + */ +static inline u32 +hicn_hashtb_bucket_idx (hicn_hashtb_h h, u64 hashval) +{ + return ((u32) (hashval & (h->ht_bucket_count - 1))); +} + +/* + * Return a hash node struct from the free list, or NULL. Note that the + * returned struct is _not_ cleared/zeroed - init is up to the caller. + */ +static inline hicn_hash_node_t * +hicn_hashtb_alloc_node (hicn_hashtb_h h) +{ + hicn_hash_node_t *p = NULL; + + if (h->ht_nodes_used < h->ht_node_count) + { + pool_get_aligned (h->ht_nodes, p, 8); + h->ht_nodes_used++; + } + return (p); +} + +/* + * Release a hashtable node back to the free list when an entry is cleared + */ +void hicn_hashtb_free_node (hicn_hashtb_h h, hicn_hash_node_t * node); + +/* + * Walk a hashtable, iterating through the nodes, keeping context in 'ctx' + * between calls. + * + * Set the context value to HICN_HASH_WALK_CTX_INITIAL to start an iteration. + */ +int +hicn_hashtb_next_node (hicn_hashtb_h h, hicn_hash_node_t ** pnode, u64 * ctx); + + +int +hicn_hashtb_key_to_str (hicn_hashtb_h h, const hicn_hash_node_t * node, + char *buf, int bufsize, int must_fit); + +/* + * single hash full name can pass offset for two hashes calculation in case + * we use CS and PIT in a two steps hashes (prefix + seqno) + */ +always_inline int +hicn_hashtb_fullhash (const u8 * name, u16 namelen, u64 * name_hash) +{ + *name_hash = hicn_hashtb_hash_name (name, namelen); + return (*name_hash != (-1LL) ? HICN_ERROR_NONE : HICN_ERROR_HASHTB_INVAL); +} + +#endif /* // __HICN_HASHTB_H__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/hicn.api b/hicn-plugin/src/hicn.api new file mode 100755 index 000000000..e7d7d33c4 --- /dev/null +++ b/hicn-plugin/src/hicn.api @@ -0,0 +1,538 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +define hicn_api_node_params_set +{ + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; + + /* Enable / disable ICN forwarder in VPP */ + u8 enable_disable; + + /* PIT maximum size, otherwise -1 to assign default value */ + i32 pit_max_size; + + /* CS maximum size, otherwise -1 to assign default value */ + i32 cs_max_size; + + /* Portion of CS reserved to application, otherwise -1 to assign default value */ + i32 cs_reserved_app; + + /* Default PIT entry lifetime */ + f64 pit_dflt_lifetime_sec; + + /* Lower bound on PIT entry lifetime */ + f64 pit_min_lifetime_sec; + + /* Upper bound on PIT entry lifetime */ + f64 pit_max_lifetime_sec; +}; + +define hicn_api_node_params_set_reply +{ + /* From the request */ + u32 context; + + /* Return value, zero means all OK */ + i32 retval; +}; + +define hicn_api_node_params_get +{ + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; +}; + +define hicn_api_node_params_get_reply +{ + /* From the request */ + u32 context; + + /* Return value, zero means all OK */ + i32 retval; + + /* Enabled / disabled flag */ + u8 is_enabled; + + /* compile-time plugin features */ + u8 feature_cs; + + /* Number of VPP workers */ + u32 worker_count; + + /* PIT maximum size, otherwise -1 to assign default value */ + u32 pit_max_size; + + /* CS maximum size, otherwise -1 to assign default value */ + u32 cs_max_size; + + /* Default PIT entry lifetime */ + f64 pit_dflt_lifetime_sec; + + /* Lower bound on PIT entry lifetime */ + f64 pit_min_lifetime_sec; + + /* Upper bound on PIT entry lifetime */ + f64 pit_max_lifetime_sec; +}; + +define hicn_api_node_stats_get +{ + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; +}; + +define hicn_api_node_stats_get_reply +{ + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; + + /* Return value, zero means all OK */ + i32 retval; + + /* ICN packets processed */ + u64 pkts_processed; + + /* ICN interests forwarded */ + u64 pkts_interest_count; + + /* ICN data msgs forwarded */ + u64 pkts_data_count; + + /* ICN cached data msg replies */ + u64 pkts_from_cache_count; + + /* ICN no PIT entry drops */ + u64 pkts_no_pit_count; + + /* ICN expired PIT entries */ + u64 pit_expired_count; + + /* ICN expired CS entries */ + u64 cs_expired_count; + + /* ICN LRU CS entries freed */ + u64 cs_lru_count; + + /* ICN msgs dropped due to no packet buffers */ + u64 pkts_drop_no_buf; + + /* ICN Interest messages aggregated in PIT */ + u64 interests_aggregated; + + /* ICN Interest messages retransmitted */ + u64 interests_retx; + + /* ICN Interest messages colliding in hashtb */ + u64 interests_hash_collision; + + /* Number of entries in PIT at the present moment */ + u64 pit_entries_count; + + /* Number of entries in CS at the present moment */ + u64 cs_entries_count; + + /* Number of entries in CS at the present moment */ + u64 cs_entries_ntw_count; +}; + +define hicn_api_face_ip_add +{ + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; + + /* IP local address */ + u64 nh_addr[2]; + + /* IPv4 local port number */ + u32 swif; +}; + +define hicn_api_face_ip_add_reply +{ + /* From the request */ + u32 context; + + /* Return value: new Face ID, ~0 means no Face was created */ + u32 faceid; + + /* Return value, zero means all OK */ + i32 retval; +}; + +define hicn_api_face_ip_del +{ + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; + + /* A Face ID to be deleted */ + u16 faceid; +}; + +define hicn_api_face_ip_del_reply +{ + /* From the request */ + u32 context; + + /* Return value, zero means all OK */ + i32 retval; +}; + +define hicn_api_face_ip_params_get +{ + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; + + /* A Face to be retrieved */ + u16 faceid; +}; + +define hicn_api_face_ip_params_get_reply +{ + /* From the request */ + u32 context; + + /* Return value, zero means all OK */ + i32 retval; + + /* IP local address */ + u64 nh_addr[2]; + + /* VPP interface (index) associated with the face */ + u32 swif; + + /* Face flags */ + u32 flags; +}; + +define hicn_api_route_nhops_add +{ + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; + + /* Prefix to be added to the FIB */ + u64 prefix[2]; + + /* Length of the prefix */ + u8 len; + + /* A Face ID to the next hop forwarder for the specified prefix */ + u32 face_ids[7]; + + /* Number of face to add */ + u8 n_faces; +}; + +define hicn_api_route_nhops_add_reply +{ + /* From the request */ + u32 context; + + /* Return value, zero means all OK */ + i32 retval; +}; + +define hicn_api_route_del +{ + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; + + /* Prefix to be added to the FIB */ + u64 prefix[2]; + + /* Length of the prefix */ + u8 len; +}; + +define hicn_api_route_del_reply +{ + /* From the request */ + u32 context; + + /* Return value, zero means all OK */ + i32 retval; +}; + +define hicn_api_route_nhop_del +{ + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; + + /* Prefix to be added to the FIB */ + u64 prefix[2]; + + /* Length of the prefix */ + u8 len; + + /* Specific next-hop to be removed */ + u16 faceid; +}; + +define hicn_api_route_nhop_del_reply +{ + /* From the request */ + u32 context; + + /* Return value, zero means all OK */ + i32 retval; +}; + +define hicn_api_route_get +{ + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; + + /* Route prefix */ + u64 prefix[2]; + + /* Prefix len */ + u8 len; +}; + +define hicn_api_route_get_reply +{ + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; + + /* List of faces pointing to the next hops */ + u16 faceids[1000]; + + /* Strategy */ + u32 strategy_id; + + /* Return value, zero means all OK */ + i32 retval; +}; + +define hicn_api_strategies_get +{ + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; +}; + +define hicn_api_strategies_get_reply +{ + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; + + /* Number of available strategies */ + u8 n_strategies; + + /* Strategies */ + u32 strategy_id[256]; + + /* Return value, zero means all OK */ + i32 retval; +}; + +define hicn_api_strategy_get +{ + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; + + /* Route prefix */ + u32 strategy_id; +}; + +define hicn_api_strategy_get_reply +{ + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; + + /* Strategy description */ + u8 description[200]; + + /* Return value, zero means all OK */ + i32 retval; +}; + +define hicn_api_punting_add +{ + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; + + /* Prefix to match */ + u64 prefix[2]; + + /* Subnet */ + u8 len; + + /* Interface id */ + u32 swif; +}; + +define hicn_api_punting_add_reply +{ + /* From the request */ + u32 context; + + /* Return value, zero means all OK */ + i32 retval; +}; + +define hicn_api_punting_del +{ + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; + + /* Prefix to match */ + u64 prefix[2]; + + /* Subnet */ + u8 len; + + /* Interface id */ + u32 swif; +}; + +define hicn_api_punting_del_reply +{ + /* From the request */ + u32 context; + + /* Return value, zero means all OK */ + i32 retval; +}; + +define hicn_api_register_prod_app +{ + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u64 context; + + /* Prefix to match */ + u64 prefix[2]; + + /* Subnet */ + u8 len; + + /* sw_if id */ + u32 swif; + + /* CS memory reserved -- in number of packets */ + u32 cs_reserved; +}; + +define hicn_api_register_prod_app_reply +{ + /* From the request */ + u64 context; + + /* Return value, zero means all OK */ + i32 retval; + + /* Actual CS memory reserved -- in number of packets */ + u32 cs_reserved; + + /* Prod address (ipv4 or ipv6) */ + u64 prod_addr[2]; + + /* Return value: new Face ID, ~0 means no Face was created */ + u32 faceid; +}; + +define hicn_api_register_cons_app +{ + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u64 context; + + /* swif */ + u32 swif; +}; + +define hicn_api_register_cons_app_reply +{ + /* From the request */ + u64 context; + + /* Return value, zero means all OK */ + i32 retval; + + /* Ip4 address */ + u32 src_addr4; + + /* Ip6 address */ + u64 src_addr6[2]; + + /* Return value: new Face ID, ~0 means no Face was created */ + u32 faceid; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/hicn.c b/hicn-plugin/src/hicn.c new file mode 100755 index 000000000..a7b04de74 --- /dev/null +++ b/hicn-plugin/src/hicn.c @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "hicn.h" +#include "params.h" +#include "infra.h" +#include "strategy_dpo_manager.h" +#include "mgmt.h" +#include "punt.h" +#include "error.h" +#include "faces/app/address_mgr.h" +#include "face_db.h" +#include "faces/udp/face_udp.h" + +hicn_main_t hicn_main; +/* Module vars */ +int hicn_infra_fwdr_initialized = 0; + +/* + * Global time counters we're trying out for opportunistic hashtable + * expiration. + */ +uint16_t hicn_infra_fast_timer; /* Counts at 1 second intervals */ +uint16_t hicn_infra_slow_timer; /* Counts at 1 minute intervals */ + +hicn_face_bucket_t *hicn_face_bucket_pool; + +/* + * Init hicn forwarder with configurable PIT, CS sizes + */ +static int +hicn_infra_fwdr_init (uint32_t shard_pit_size, uint32_t shard_cs_size, + uint32_t cs_reserved) +{ + int ret = 0; + + if (hicn_infra_fwdr_initialized) + { + ret = HICN_ERROR_FWD_ALREADY_ENABLED; + goto done; + } + /* Init per worker limits */ + hicn_infra_pit_size = shard_pit_size; + hicn_infra_cs_size = shard_cs_size; + + /* Init the global time-compression counters */ + hicn_infra_fast_timer = 1; + hicn_infra_slow_timer = 1; + + ret = hicn_pit_create (&hicn_main.pitcs, hicn_infra_pit_size); + hicn_pit_set_lru_max (&hicn_main.pitcs, + hicn_infra_cs_size - + (hicn_infra_cs_size * cs_reserved / 100)); + hicn_pit_set_lru_app_max (&hicn_main.pitcs, + hicn_infra_cs_size * cs_reserved / 100); + +done: + if ((ret == HICN_ERROR_NONE) && !hicn_infra_fwdr_initialized) + { + hicn_infra_fwdr_initialized = 1; + } + return (ret); +} + +/* + * Action function shared between message handler and debug CLI NOTICE: we're + * only 'enabling' now + */ +int +hicn_infra_plugin_enable_disable (int enable_disable, + int pit_size_req, + f64 pit_dflt_lifetime_sec_req, + f64 pit_min_lifetime_sec_req, + f64 pit_max_lifetime_sec_req, + int cs_size_req, int cs_reserved_app) +{ + int ret = 0; + + hicn_main_t *sm = &hicn_main; + uint32_t pit_size, cs_size, cs_reserved; + + /* Notice if we're already enabled... */ + if (sm->is_enabled) + { + ret = HICN_ERROR_FWD_ALREADY_ENABLED; + goto done; + } + /* Set up params and call fwdr_init set up PIT/CS, forwarder nodes */ + + /* Check the range and assign some globals */ + if (pit_min_lifetime_sec_req < 0) + { + sm->pit_lifetime_min_ms = HICN_PARAM_PIT_LIFETIME_DFLT_MIN_MS; + } + else + { + if (pit_min_lifetime_sec_req < HICN_PARAM_PIT_LIFETIME_BOUND_MIN_SEC || + pit_min_lifetime_sec_req > HICN_PARAM_PIT_LIFETIME_BOUND_MAX_SEC) + { + ret = HICN_ERROR_PIT_CONFIG_MINLT_OOB; + goto done; + } + sm->pit_lifetime_min_ms = pit_min_lifetime_sec_req * SEC_MS; + } + + if (pit_max_lifetime_sec_req < 0) + { + sm->pit_lifetime_max_ms = HICN_PARAM_PIT_LIFETIME_DFLT_MAX_MS; + } + else + { + if (pit_max_lifetime_sec_req < HICN_PARAM_PIT_LIFETIME_BOUND_MIN_SEC || + pit_max_lifetime_sec_req > HICN_PARAM_PIT_LIFETIME_BOUND_MAX_SEC) + { + ret = HICN_ERROR_PIT_CONFIG_MAXLT_OOB; + goto done; + } + sm->pit_lifetime_max_ms = pit_max_lifetime_sec_req * SEC_MS; + } + if (sm->pit_lifetime_min_ms > sm->pit_lifetime_max_ms) + { + ret = HICN_ERROR_PIT_CONFIG_MINMAXLT; + goto done; + } + if (pit_dflt_lifetime_sec_req < 0) + { + sm->pit_lifetime_dflt_ms = HICN_PARAM_PIT_LIFETIME_DFLT_DFLT_MS; + } + else + { + sm->pit_lifetime_dflt_ms = pit_dflt_lifetime_sec_req * SEC_MS; + } + if (sm->pit_lifetime_dflt_ms < sm->pit_lifetime_min_ms || + sm->pit_lifetime_dflt_ms > sm->pit_lifetime_max_ms) + { + ret = HICN_ERROR_PIT_CONFIG_DFTLT_OOB; + goto done; + } + if (pit_size_req < 0) + { + pit_size = HICN_PARAM_PIT_ENTRIES_DFLT; + } + else + { + if (pit_size_req < HICN_PARAM_PIT_ENTRIES_MIN || + pit_size_req > HICN_PARAM_PIT_ENTRIES_MAX) + { + ret = HICN_ERROR_PIT_CONFIG_SIZE_OOB; + goto done; + } + pit_size = (uint32_t) pit_size_req; + } + + if (cs_size_req < 0) + { + cs_size = HICN_PARAM_CS_ENTRIES_DFLT; + } + else + { + if (cs_size_req > HICN_PARAM_CS_ENTRIES_MAX) + { + ret = HICN_ERROR_CS_CONFIG_SIZE_OOB; + goto done; + } + cs_size = (uint32_t) cs_size_req; + } + + if (cs_reserved_app < 0) + { + cs_reserved = HICN_PARAM_CS_RESERVED_APP; + } + else + { + if (cs_reserved_app >= 100) + ret = HICN_ERROR_CS_CONFIG_RESERVED_OOB; + cs_reserved = cs_reserved_app; + } + + ret = hicn_infra_fwdr_init (pit_size, cs_size, cs_reserved); + + hicn_face_db_init (pit_size); + + if (ret != HICN_ERROR_NONE) + { + goto done; + } + sm->is_enabled = 1; + + hicn_face_udp_init_internal (); + +done: + + return (ret); +} + +/* + * Init entry-point for the icn plugin + */ +static clib_error_t * +hicn_init (vlib_main_t * vm) +{ + clib_error_t *error = 0; + + hicn_main_t *sm = &hicn_main; + + /* Init other elements in the 'main' struct */ + sm->is_enabled = 0; + + error = hicn_api_plugin_hookup (vm); + + /* Init the hash table */ + hicn_punt_init (vm); + + /* Init the dpo module */ + hicn_dpos_init (); + + /* Init the app manager */ + address_mgr_init (); + + hicn_face_module_init (vm); + + return error; +} + +VLIB_INIT_FUNCTION (hicn_init); + +/* *INDENT-OFF* */ +VLIB_PLUGIN_REGISTER() = +{ + .description = "hICN forwarder" +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/hicn.h b/hicn-plugin/src/hicn.h new file mode 100755 index 000000000..02a3dfa52 --- /dev/null +++ b/hicn-plugin/src/hicn.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_H__ +#define __HICN_H__ + +#include + +#include +#include +#include +#include +#include +#include + +/* Helper for avoiding warnings about type-punning */ +#define UNION_CAST(x, destType) \ + (((union {__typeof__(x) a; destType b;})x).b) + +/* + * Update CMakeLists.txt as we have to manually replace the type for + * vppapigen + */ +typedef u8 weight_t; + +#define ISV6(isv6, dov6, dov4) isv6 ? dov6 : dov4 +#define HICN_IS_NAMEHASH_CACHED(b) (((u64)(b->opaque2)[0] != 0) || ((u64)(b->opaque2)[1] != 0)) + +#ifndef VLIB_BUFFER_MIN_CHAIN_SEG_SIZE +#define VLIB_BUFFER_MIN_CHAIN_SEG_SIZE (128) +#endif + +/* The following is stored in the opaque2 field in the vlib_buffer_t */ +typedef struct +{ + /* hash of the name */ + u64 name_hash; + + /* ids to prefetch a PIT/CS entry */ + u32 node_id; + u32 bucket_id; + u8 hash_entry_id; + u8 hash_bucket_flags; + + u8 is_appface; /* 1 the incoming face is an + * application face, 0 otherwise */ + u8 dpo_ctx_id; /* used for data path */ + u8 vft_id; /* " */ + + dpo_id_t face_dpo_id; /* ingress face ,sizeof(iface_dpo_id) + * <= sizeof(u64) */ + + hicn_type_t type; +} hicn_buffer_t; + +STATIC_ASSERT (sizeof (hicn_buffer_t) <= + STRUCT_SIZE_OF (vlib_buffer_t, opaque2), + "hICN buffer opaque2 meta-data too large for vlib_buffer"); + + +always_inline hicn_buffer_t * +hicn_get_buffer (vlib_buffer_t * b0) +{ + return (hicn_buffer_t *) & (b0->opaque2[0]); +} + +#endif /* __HICN_H__ */ + + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/hicn_all_api_h.h b/hicn-plugin/src/hicn_all_api_h.h new file mode 100755 index 000000000..1263ea4a2 --- /dev/null +++ b/hicn-plugin/src/hicn_all_api_h.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/hicn_api.c b/hicn-plugin/src/hicn_api.c new file mode 100755 index 000000000..8becde12c --- /dev/null +++ b/hicn-plugin/src/hicn_api.c @@ -0,0 +1,570 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hicn.h" +#include "faces/ip/face_ip.h" +#include "infra.h" +#include "parser.h" +#include "mgmt.h" +#include "strategy_dpo_manager.h" +#include "strategy_dpo_ctx.h" +#include "strategy.h" +#include "pg.h" +#include "error.h" +#include "punt.h" +#include "faces/app/face_prod.h" +#include "faces/app/face_cons.h" +#include "route.h" + +/* define message IDs */ +#include + +/* define generated endian-swappers */ +#define vl_endianfun +#include +#undef vl_endianfun + +/* instantiate all the print functions we know about */ +#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) +#define vl_printfun +#include +#undef vl_printfun + +/* Get the API version number */ +#define vl_api_version(n, v) static u32 api_version=(v); +#include +#undef vl_api_version + +#define REPLY_MSG_ID_BASE sm->msg_id_base +#include + +/****** List of message types that this plugin understands ******/ + +#define foreach_hicn_plugin_api_msg \ + _(HICN_API_NODE_PARAMS_SET, hicn_api_node_params_set) \ + _(HICN_API_NODE_PARAMS_GET, hicn_api_node_params_get) \ + _(HICN_API_NODE_STATS_GET, hicn_api_node_stats_get) \ + _(HICN_API_FACE_IP_ADD, hicn_api_face_ip_add) \ + _(HICN_API_FACE_IP_DEL, hicn_api_face_ip_del) \ + _(HICN_API_FACE_IP_PARAMS_GET, hicn_api_face_ip_params_get) \ + _(HICN_API_ROUTE_GET, hicn_api_route_get) \ + _(HICN_API_ROUTE_NHOPS_ADD, hicn_api_route_nhops_add) \ + _(HICN_API_ROUTE_DEL, hicn_api_route_del) \ + _(HICN_API_ROUTE_NHOP_DEL, hicn_api_route_nhop_del) \ + _(HICN_API_STRATEGIES_GET, hicn_api_strategies_get) \ + _(HICN_API_STRATEGY_GET, hicn_api_strategy_get) \ + _(HICN_API_PUNTING_ADD, hicn_api_punting_add) \ + _(HICN_API_PUNTING_DEL, hicn_api_punting_del) \ + _(HICN_API_REGISTER_PROD_APP, hicn_api_register_prod_app) \ + _(HICN_API_REGISTER_CONS_APP, hicn_api_register_cons_app) + + +/****** SUPPORTING FUNCTION DECLARATIONS ******/ + +/* + * Convert a unix return code to a vnet_api return code. Currently stubby: + * should have more cases. + */ +always_inline vnet_api_error_t +hicn_face_api_entry_params_serialize (hicn_face_id_t faceid, + vl_api_hicn_api_face_ip_params_get_reply_t + * reply); + + +/****************** API MESSAGE HANDLERS ******************/ + +/****** NODE ******/ + +static void +vl_api_hicn_api_node_params_set_t_handler (vl_api_hicn_api_node_params_set_t * + mp) +{ + vl_api_hicn_api_node_params_set_reply_t *rmp; + int rv; + + hicn_main_t *sm = &hicn_main; + + int pit_max_size = clib_net_to_host_i32 (mp->pit_max_size); + f64 pit_dflt_lifetime_sec = mp->pit_dflt_lifetime_sec; + f64 pit_min_lifetime_sec = mp->pit_min_lifetime_sec; + f64 pit_max_lifetime_sec = mp->pit_max_lifetime_sec; + int cs_max_size = clib_net_to_host_i32 (mp->cs_max_size); + int cs_reserved_app = clib_net_to_host_i32 (mp->cs_reserved_app); + + cs_reserved_app = cs_reserved_app >= 0 + && cs_reserved_app < 100 ? cs_reserved_app : HICN_PARAM_CS_RESERVED_APP; + + rv = hicn_infra_plugin_enable_disable ((int) (mp->enable_disable), + pit_max_size, + pit_dflt_lifetime_sec, + pit_min_lifetime_sec, + pit_max_lifetime_sec, + cs_max_size, cs_reserved_app); + + REPLY_MACRO (VL_API_HICN_API_NODE_PARAMS_SET_REPLY /* , rmp, mp, rv */ ); +} + +static void +vl_api_hicn_api_node_params_get_t_handler (vl_api_hicn_api_node_params_get_t * + mp) +{ + vl_api_hicn_api_node_params_get_reply_t *rmp; + int rv = HICN_ERROR_NONE; + + hicn_main_t *sm = &hicn_main; + + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_HICN_API_NODE_PARAMS_GET_REPLY, ( + { + rmp->is_enabled = sm->is_enabled; + rmp->feature_cs = HICN_FEATURE_CS; + rmp->pit_max_size = clib_host_to_net_u32 (hicn_infra_pit_size); + rmp->pit_dflt_lifetime_sec = ((f64) sm->pit_lifetime_dflt_ms) / SEC_MS; + rmp->pit_min_lifetime_sec = ((f64) sm->pit_lifetime_min_ms) / SEC_MS; + rmp->pit_max_lifetime_sec = ((f64) sm->pit_lifetime_max_ms) / SEC_MS; + rmp->cs_max_size = clib_host_to_net_u32 (hicn_infra_cs_size); + rmp->retval = clib_host_to_net_i32 (rv); + })); + /* *INDENT-ON* */ +} + +static void +vl_api_hicn_api_node_stats_get_t_handler (vl_api_hicn_api_node_stats_get_t * + mp) +{ + vl_api_hicn_api_node_stats_get_reply_t *rmp; + int rv = HICN_ERROR_NONE; + + hicn_main_t *sm = &hicn_main; + + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_HICN_API_NODE_STATS_GET_REPLY, ( + { + rv = hicn_mgmt_node_stats_get (rmp); + rmp->retval =clib_host_to_net_i32 (rv); + })); + /* *INDENT-ON* */ +} + + +/****** FACE *******/ + +static void +vl_api_hicn_api_face_ip_add_t_handler (vl_api_hicn_api_face_ip_add_t * mp) +{ + vl_api_hicn_api_face_ip_add_reply_t *rmp; + int rv; + + hicn_main_t *sm = &hicn_main; + + hicn_face_id_t faceid = HICN_FACE_NULL; + ip46_address_t nh_addr; + nh_addr.as_u64[0] = clib_net_to_host_u64 (((u64 *) (&mp->nh_addr))[0]); + nh_addr.as_u64[1] = clib_net_to_host_u64 (((u64 *) (&mp->nh_addr))[1]); + + u32 swif = clib_net_to_host_u32 (mp->swif); + rv = hicn_face_ip_add (&nh_addr, NULL, swif, &faceid); + + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_HICN_API_FACE_IP_ADD_REPLY /* , rmp, mp, rv */ ,( + { + rmp->faceid = clib_host_to_net_u16 ((u16) faceid); + })); + /* *INDENT-ON* */ +} + +static void +vl_api_hicn_api_face_ip_del_t_handler (vl_api_hicn_api_face_ip_del_t * mp) +{ + vl_api_hicn_api_face_ip_del_reply_t *rmp; + int rv = HICN_ERROR_NONE; + + hicn_main_t *sm = &hicn_main; + + hicn_face_id_t faceid = clib_net_to_host_u16 (mp->faceid); + rv = hicn_face_del (faceid); + + REPLY_MACRO (VL_API_HICN_API_FACE_IP_DEL_REPLY /* , rmp, mp, rv */ ); + +} + +static void + vl_api_hicn_api_face_ip_params_get_t_handler + (vl_api_hicn_api_face_ip_params_get_t * mp) +{ + vl_api_hicn_api_face_ip_params_get_reply_t *rmp; + int rv = 0; + + hicn_main_t *sm = &hicn_main; + + hicn_face_id_t faceid = clib_net_to_host_u16 (mp->faceid); + + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_HICN_API_FACE_IP_PARAMS_GET_REPLY, ( + { + rv = hicn_face_api_entry_params_serialize(faceid, rmp); + rmp->retval = clib_host_to_net_u32(rv); + })); + /* *INDENT-ON* */ +} + +/****** ROUTE *******/ + +static void +vl_api_hicn_api_route_nhops_add_t_handler (vl_api_hicn_api_route_nhops_add_t + * mp) +{ + vl_api_hicn_api_route_nhops_add_reply_t *rmp; + int rv = HICN_ERROR_NONE; + hicn_face_id_t face_ids[HICN_PARAM_FIB_ENTRY_NHOPS_MAX]; + + hicn_main_t *sm = &hicn_main; + + ip46_address_t prefix; + prefix.as_u64[0] = clib_net_to_host_u64 (((u64 *) (&mp->prefix))[0]); + prefix.as_u64[1] = clib_net_to_host_u64 (((u64 *) (&mp->prefix))[1]); + + u8 len = mp->len; + u8 n_faces = mp->n_faces; + + for (int i = 0; i < HICN_PARAM_FIB_ENTRY_NHOPS_MAX; i++) + { + face_ids[i] = clib_net_to_host_u16 (mp->face_ids[i]); + } + + if ((face_ids == NULL) || (n_faces > HICN_PARAM_FIB_ENTRY_NHOPS_MAX)) + { + rv = VNET_API_ERROR_INVALID_ARGUMENT; + } + if (rv == HICN_ERROR_NONE) + { + rv = hicn_route_add (face_ids, n_faces, &prefix, len); + + if (rv == HICN_ERROR_ROUTE_ALREADY_EXISTS) + { + rv = hicn_route_add_nhops (face_ids, n_faces, &prefix, len); + } + } + REPLY_MACRO (VL_API_HICN_API_ROUTE_NHOPS_ADD_REPLY /* , rmp, mp, rv */ ); +} + + +static void vl_api_hicn_api_route_del_t_handler + (vl_api_hicn_api_route_del_t * mp) +{ + vl_api_hicn_api_route_del_reply_t *rmp; + int rv = HICN_ERROR_NONE; + + hicn_main_t *sm = &hicn_main; + + ip46_address_t prefix; + prefix.as_u64[0] = clib_net_to_host_u64 (((u64 *) (&mp->prefix))[0]); + prefix.as_u64[1] = clib_net_to_host_u64 (((u64 *) (&mp->prefix))[1]); + u8 len = mp->len; + + rv = hicn_route_del (&prefix, len); + + REPLY_MACRO (VL_API_HICN_API_ROUTE_DEL_REPLY /* , rmp, mp, rv */ ); +} + +static void vl_api_hicn_api_route_nhop_del_t_handler + (vl_api_hicn_api_route_nhop_del_t * mp) +{ + vl_api_hicn_api_route_nhop_del_reply_t *rmp; + int rv = HICN_ERROR_NONE; + + hicn_main_t *sm = &hicn_main; + + ip46_address_t prefix; + prefix.as_u64[0] = clib_net_to_host_u64 (((u64 *) (&mp->prefix))[0]); + prefix.as_u64[1] = clib_net_to_host_u64 (((u64 *) (&mp->prefix))[1]); + u8 len = mp->len; + hicn_face_id_t faceid = clib_net_to_host_u32 (mp->faceid); + + + rv = hicn_route_del_nhop (&prefix, len, faceid); + + REPLY_MACRO (VL_API_HICN_API_ROUTE_NHOP_DEL_REPLY /* , rmp, mp, rv */ ); +} + +static void vl_api_hicn_api_route_get_t_handler + (vl_api_hicn_api_route_get_t * mp) +{ + vl_api_hicn_api_route_get_reply_t *rmp; + int rv = HICN_ERROR_NONE; + + hicn_main_t *sm = &hicn_main; + + ip46_address_t prefix; + prefix.as_u64[0] = clib_net_to_host_u64 (((u64 *) (&mp->prefix))[0]); + prefix.as_u64[1] = clib_net_to_host_u64 (((u64 *) (&mp->prefix))[1]); + u8 len = mp->len; + const dpo_id_t *hicn_dpo_id; + const hicn_dpo_vft_t *hicn_dpo_vft; + hicn_dpo_ctx_t *hicn_dpo_ctx; + u32 fib_index; + + rv = hicn_route_get_dpo (&prefix, len, &hicn_dpo_id, &fib_index); + + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_HICN_API_ROUTE_GET_REPLY, ( + { + if (rv == HICN_ERROR_NONE) + { + hicn_dpo_vft = hicn_dpo_get_vft(hicn_dpo_id->dpoi_index); + hicn_dpo_ctx = hicn_dpo_vft->hicn_dpo_get_ctx(hicn_dpo_id->dpoi_index); + for (int i = 0; i < hicn_dpo_ctx->entry_count; i++) + { + if (dpo_id_is_valid(&hicn_dpo_ctx->next_hops[i])) + { + rmp->faceids[i] =((dpo_id_t *) &hicn_dpo_ctx->next_hops[i])->dpoi_index;} + } + rmp->strategy_id = clib_host_to_net_u32(hicn_dpo_get_vft_id(hicn_dpo_id));} + })); + /* *INDENT-ON* */ +} + +static void vl_api_hicn_api_strategies_get_t_handler + (vl_api_hicn_api_strategies_get_t * mp) +{ + vl_api_hicn_api_strategies_get_reply_t *rmp; + int rv = HICN_ERROR_NONE; + + hicn_main_t *sm = &hicn_main; + + int n_strategies = hicn_strategy_get_all_available (); + + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_HICN_API_STRATEGIES_GET_REPLY/* , rmp, mp, rv */ ,( + { + int j = 0; + for (u32 i = 0; i < (u32) n_strategies; i++) + { + if (hicn_dpo_strategy_id_is_valid (i) == HICN_ERROR_NONE) + { + rmp->strategy_id[j] = clib_host_to_net_u32 (i); j++;} + } + rmp->n_strategies = n_strategies; + })); + /* *INDENT-ON* */ +} + +static void vl_api_hicn_api_strategy_get_t_handler + (vl_api_hicn_api_strategy_get_t * mp) +{ + vl_api_hicn_api_strategy_get_reply_t *rmp; + int rv = HICN_ERROR_NONE; + + hicn_main_t *sm = &hicn_main; + + u32 strategy_id = clib_net_to_host_u32 (mp->strategy_id); + rv = hicn_dpo_strategy_id_is_valid (strategy_id); + + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_HICN_API_STRATEGY_GET_REPLY /* , rmp, mp, rv */ ,( + { + if (rv == HICN_ERROR_NONE) + { + const hicn_dpo_vft_t * hicn_dpo_vft = + hicn_dpo_get_vft (strategy_id); + hicn_dpo_vft->format_hicn_dpo (rmp->description, 0);} + })); + /* *INDENT-ON* */ +} + +/****** PUNTING *******/ + +static void vl_api_hicn_api_punting_add_t_handler + (vl_api_hicn_api_punting_add_t * mp) +{ + vl_api_hicn_api_punting_add_reply_t *rmp; + int rv = HICN_ERROR_NONE; + vlib_main_t *vm = vlib_get_main (); + + hicn_main_t *sm = &hicn_main; + + ip46_address_t prefix; + prefix.as_u64[0] = clib_net_to_host_u64 (((u64 *) (&mp->prefix))[0]); + prefix.as_u64[1] = clib_net_to_host_u64 (((u64 *) (&mp->prefix))[1]); + u8 subnet_mask = mp->len; + u32 swif = clib_net_to_host_u32 (mp->swif); + + rv = + hicn_punt_interest_data_for_ethernet (vm, &prefix, subnet_mask, swif, 0); + + REPLY_MACRO (VL_API_HICN_API_PUNTING_ADD_REPLY /* , rmp, mp, rv */ ); +} + +static void vl_api_hicn_api_punting_del_t_handler + (vl_api_hicn_api_punting_del_t * mp) +{ + vl_api_hicn_api_punting_del_reply_t *rmp; + int rv = HICN_ERROR_NONE; + + hicn_main_t *sm = &hicn_main; + + rv = HICN_ERROR_NONE; + + REPLY_MACRO (VL_API_HICN_API_ROUTE_DEL_REPLY /* , rmp, mp, rv */ ); +} + +/************* APP FACE ****************/ + +static void vl_api_hicn_api_register_prod_app_t_handler + (vl_api_hicn_api_register_prod_app_t * mp) +{ + vl_api_hicn_api_register_prod_app_reply_t *rmp; + int rv = HICN_ERROR_NONE; + + hicn_main_t *sm = &hicn_main; + + hicn_prefix_t prefix; + prefix.name.as_u64[0] = clib_net_to_host_u64 (((u64 *) (&mp->prefix))[0]); + prefix.name.as_u64[1] = clib_net_to_host_u64 (((u64 *) (&mp->prefix))[1]); + prefix.len = mp->len; + u32 swif = clib_net_to_host_u32 (mp->swif); + u32 cs_reserved = clib_net_to_host_u32 (mp->cs_reserved); + u32 faceid; + + ip46_address_t prod_addr; + ip46_address_reset (&prod_addr); + rv = hicn_face_prod_add (&prefix, swif, &cs_reserved, &prod_addr, &faceid); + + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_HICN_API_REGISTER_PROD_APP_REPLY, ( + { + rmp->prod_addr[0] = prod_addr.as_u64[0]; + rmp->prod_addr[1] = prod_addr.as_u64[1]; + rmp->cs_reserved = clib_net_to_host_u32(cs_reserved); + rmp->faceid = clib_net_to_host_u32(faceid); + })); + /* *INDENT-ON* */ +} + +static void vl_api_hicn_api_register_cons_app_t_handler + (vl_api_hicn_api_register_cons_app_t * mp) +{ + vl_api_hicn_api_register_cons_app_reply_t *rmp; + int rv = HICN_ERROR_NONE; + + hicn_main_t *sm = &hicn_main; + ip4_address_t src_addr4; + ip6_address_t src_addr6; + src_addr4.as_u32 = (u32) 0; + src_addr6.as_u64[0] = (u64) 0; + src_addr6.as_u64[1] = (u64) 1; + + u32 swif = clib_net_to_host_u32 (mp->swif); + u32 faceid; + + rv = hicn_face_cons_add (&src_addr4, &src_addr6, swif, &faceid); + + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_HICN_API_REGISTER_CONS_APP_REPLY, ( + { + rmp->src_addr4 = clib_net_to_host_u32(src_addr4.as_u32); + rmp->src_addr6[0] = clib_net_to_host_u64(src_addr6.as_u64[0]); + rmp->src_addr6[1] = clib_net_to_host_u64(src_addr6.as_u64[1]); + rmp->faceid = clib_net_to_host_u32(faceid); + })); + /* *INDENT-ON* */ +} + +/************************************************************************************/ + +/* Set up the API message handling tables */ +clib_error_t * +hicn_api_plugin_hookup (vlib_main_t * vm) +{ + hicn_main_t *sm = &hicn_main; + + /* Get a correctly-sized block of API message decode slots */ + u8 *name = format (0, "hicn_%08x%c", api_version, 0); + sm->msg_id_base = vl_msg_api_get_msg_ids ((char *) name, + VL_MSG_FIRST_AVAILABLE); + vec_free (name); + +#define _(N, n) \ + vl_msg_api_set_handlers(sm->msg_id_base + VL_API_##N, \ + #n, \ + vl_api_##n##_t_handler, \ + vl_noop_handler, \ + vl_api_##n##_t_endian, \ + vl_api_##n##_t_print, \ + sizeof(vl_api_##n##_t), 1); + foreach_hicn_plugin_api_msg; +#undef _ + + return 0; +} + + + + + + + +/******************* SUPPORTING FUNCTIONS *******************/ + +/* + * Binary serialization for get face configuration API. for the moment + * assuming only ip faces here. To be completed with othet types of faces + */ +vnet_api_error_t +hicn_face_api_entry_params_serialize (hicn_face_id_t faceid, + vl_api_hicn_api_face_ip_params_get_reply_t + * reply) +{ + int rv = HICN_ERROR_NONE; + + if (!reply) + { + rv = VNET_API_ERROR_INVALID_ARGUMENT; + goto done; + } + hicn_face_t *face = hicn_dpoi_get_from_idx (faceid); + + ip_adjacency_t *ip_adj = adj_get (face->shared.adj); + + if (ip_adj != NULL) + { + reply->nh_addr[0] = + clib_host_to_net_u64 (ip_adj->sub_type.nbr.next_hop.as_u64[0]); + reply->nh_addr[1] = + clib_host_to_net_u64 (ip_adj->sub_type.nbr.next_hop.as_u64[1]); + reply->swif = clib_host_to_net_u32 (face->shared.sw_if); + reply->flags = clib_host_to_net_u32 (face->shared.flags); + } + else + rv = HICN_ERROR_FACE_IP_ADJ_NOT_FOUND; + +done: + return (rv); +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/hicn_api.h b/hicn-plugin/src/hicn_api.h new file mode 100755 index 000000000..79b561be4 --- /dev/null +++ b/hicn-plugin/src/hicn_api.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_API_H__ +#define __HICN_API_H__ + +#define HICN_STRATEGY_NULL ~0 + +/* define message structures */ +#define vl_typedefs +#include +#undef vl_typedefs + +#endif /* // __HICN_API_H___ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/hicn_api_test.c b/hicn-plugin/src/hicn_api_test.c new file mode 100755 index 000000000..9d4519bf4 --- /dev/null +++ b/hicn-plugin/src/hicn_api_test.c @@ -0,0 +1,1046 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#define __plugin_msg_base hicn_test_main.msg_id_base +#include + + +#include +#include "error.h" + +// uword unformat_sw_if_index(unformat_input_t * input, va_list * args); + +/* Declare message IDs */ +#include "hicn_msg_enum.h" + +/* declare message handlers for each api */ + +#define vl_endianfun /* define message structures */ +#include "hicn_all_api_h.h" +#undef vl_endianfun + +/* instantiate all the print functions we know about */ +#define vl_print(handle, ...) +#define vl_printfun +#include "hicn_all_api_h.h" +#undef vl_printfun + +/* Get the API version number. */ +#define vl_api_version(n, v) static u32 api_version=(v); +#include "hicn_all_api_h.h" +#undef vl_api_version + +/* SUPPORTING FUNCTIONS NOT LOADED BY VPP_API_TEST */ +uword +unformat_ip46_address (unformat_input_t * input, va_list * args) +{ + ip46_address_t *ip46 = va_arg (*args, ip46_address_t *); + ip46_type_t type = va_arg (*args, ip46_type_t); + if ((type != IP46_TYPE_IP6) && + unformat (input, "%U", unformat_ip4_address, &ip46->ip4)) + { + ip46_address_mask_ip4 (ip46); + return 1; + } + else if ((type != IP46_TYPE_IP4) && + unformat (input, "%U", unformat_ip6_address, &ip46->ip6)) + { + return 1; + } + return 0; +} + +///////////////////////////////////////////////////// + +#define HICN_FACE_NULL ~0 + +typedef struct +{ + /* API message ID base */ + u16 msg_id_base; + vat_main_t *vat_main; +} hicn_test_main_t; + +hicn_test_main_t hicn_test_main; + +#define foreach_standard_reply_retval_handler \ +_(hicn_api_node_params_set_reply) \ +_(hicn_api_face_ip_del_reply) \ +_(hicn_api_route_nhops_add_reply) \ +_(hicn_api_route_del_reply) \ +_(hicn_api_route_nhop_del_reply) + +#define _(n) \ + static void vl_api_##n##_t_handler \ + (vl_api_##n##_t * mp) \ + { \ + vat_main_t * vam = hicn_test_main.vat_main; \ + i32 retval = ntohl(mp->retval); \ + if (vam->async_mode) { \ + vam->async_errors += (retval < 0); \ + } else { \ + fformat (vam->ofp,"%s\n", get_error_string(retval));\ + vam->retval = retval; \ + vam->result_ready = 1; \ + } \ + } +foreach_standard_reply_retval_handler; +#undef _ + +/* + * Table of message reply handlers, must include boilerplate handlers we just + * generated + */ +#define foreach_vpe_api_reply_msg \ +_(HICN_API_NODE_PARAMS_SET_REPLY, hicn_api_node_params_set_reply) \ +_(HICN_API_NODE_PARAMS_GET_REPLY, hicn_api_node_params_get_reply) \ +_(HICN_API_NODE_STATS_GET_REPLY, hicn_api_node_stats_get_reply) \ +_(HICN_API_FACE_IP_DEL_REPLY, hicn_api_face_ip_del_reply) \ +_(HICN_API_FACE_IP_ADD_REPLY, hicn_api_face_ip_add_reply) \ +_(HICN_API_ROUTE_NHOPS_ADD_REPLY, hicn_api_route_nhops_add_reply) \ +_(HICN_API_FACE_IP_PARAMS_GET_REPLY, hicn_api_face_ip_params_get_reply) \ +_(HICN_API_ROUTE_GET_REPLY, hicn_api_route_get_reply) \ +_(HICN_API_ROUTE_DEL_REPLY, hicn_api_route_del_reply) \ +_(HICN_API_ROUTE_NHOP_DEL_REPLY, hicn_api_route_nhop_del_reply) \ +_(HICN_API_STRATEGIES_GET_REPLY, hicn_api_strategies_get_reply) \ +_(HICN_API_STRATEGY_GET_REPLY, hicn_api_strategy_get_reply) \ +_(HICN_API_REGISTER_PROD_APP_REPLY, hicn_api_register_prod_app_reply) \ +_(HICN_API_REGISTER_CONS_APP_REPLY, hicn_api_register_cons_app_reply) + + +static int +api_hicn_api_node_params_set (vat_main_t * vam) +{ + unformat_input_t *input = vam->input; + int enable_disable = 1; + int pit_size = -1, cs_size = -1; + f64 pit_dflt_lifetime_sec = -1.0f; + f64 pit_min_lifetime_sec = -1.0f, pit_max_lifetime_sec = -1.0f; + int ret; + + vl_api_hicn_api_node_params_set_t *mp; + + /* Parse args required to build the message */ + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "disable")) + { + enable_disable = 0; + } + else if (unformat (input, "PIT size %d", &pit_size)) + {; + } + else if (unformat (input, "CS size %d", &cs_size)) + {; + } + else if (unformat (input, "PIT dfltlife %f", &pit_dflt_lifetime_sec)) + {; + } + else if (unformat (input, "PIT minlife %f", &pit_min_lifetime_sec)) + {; + } + else if (unformat (input, "PIT maxlife %f", &pit_max_lifetime_sec)) + {; + } + else + { + break; + } + } + + /* Construct the API message */ + M (HICN_API_NODE_PARAMS_SET, mp); + mp->enable_disable = enable_disable; + mp->pit_max_size = clib_host_to_net_i32 (pit_size); + mp->cs_max_size = clib_host_to_net_i32 (cs_size); + mp->pit_dflt_lifetime_sec = pit_dflt_lifetime_sec; + mp->pit_min_lifetime_sec = pit_min_lifetime_sec; + mp->pit_max_lifetime_sec = pit_max_lifetime_sec; + + /* send it... */ + S (mp); + + /* Wait for a reply... */ + W (ret); + + return ret; +} + +static int +api_hicn_api_node_params_get (vat_main_t * vam) +{ + vl_api_hicn_api_node_params_get_t *mp; + int ret; + + //Construct the API message + M (HICN_API_NODE_PARAMS_GET, mp); + + /* send it... */ + S (mp); + + /* Wait for a reply... */ + W (ret); + + return ret; +} + +static void + vl_api_hicn_api_node_params_get_reply_t_handler + (vl_api_hicn_api_node_params_get_reply_t * mp) +{ + vat_main_t *vam = hicn_test_main.vat_main; + i32 retval = ntohl (mp->retval); + + if (vam->async_mode) + { + vam->async_errors += (retval < 0); + return; + } + vam->retval = retval; + vam->result_ready = 1; + + if (vam->retval < 0) + { + //vpp_api_test infra will also print out string form of error + fformat (vam->ofp, " (API call error: %d)\n", vam->retval); + return; + } + fformat (vam->ofp, + "Enabled %d\n" + " Features: cs:%d\n" + " PIT size %d\n" + " PIT lifetime dflt %.3f, min %.3f, max %.3f\n" + " CS size %d\n", + mp->is_enabled, + mp->feature_cs, + clib_net_to_host_u32 (mp->pit_max_size), + mp->pit_dflt_lifetime_sec, + mp->pit_min_lifetime_sec, + mp->pit_max_lifetime_sec, clib_net_to_host_u32 (mp->cs_max_size)); +} + +static int +api_hicn_api_node_stats_get (vat_main_t * vam) +{ + vl_api_hicn_api_node_stats_get_t *mp; + int ret; + + /* Construct the API message */ + M (HICN_API_NODE_STATS_GET, mp); + + /* send it... */ + S (mp); + + /* Wait for a reply... */ + W (ret); + + return ret; +} + +static void + vl_api_hicn_api_node_stats_get_reply_t_handler + (vl_api_hicn_api_node_stats_get_reply_t * rmp) +{ + vat_main_t *vam = hicn_test_main.vat_main; + i32 retval = ntohl (rmp->retval); + + if (vam->async_mode) + { + vam->async_errors += (retval < 0); + return; + } + vam->retval = retval; + vam->result_ready = 1; + + if (vam->retval < 0) + { + //vpp_api_test infra will also print out string form of error + fformat (vam->ofp, " (API call error: %d)\n", vam->retval); + return; + } + else + { + fformat (vam->ofp, //compare hicn_cli_show_command_fn block:should match + " PIT entries (now): %d\n" + " CS entries (now): %d\n" + " Forwarding statistics:" + " pkts_processed: %d\n" + " pkts_interest_count: %d\n" + " pkts_data_count: %d\n" + " pkts_nak_count: %d\n" + " pkts_from_cache_count: %d\n" + " pkts_nacked_interests_count: %d\n" + " pkts_nak_hoplimit_count: %d\n" + " pkts_nak_no_route_count: %d\n" + " pkts_no_pit_count: %d\n" + " pit_expired_count: %d\n" + " cs_expired_count: %d\n" + " cs_lru_count: %d\n" + " pkts_drop_no_buf: %d\n" + " interests_aggregated: %d\n" + " interests_retransmitted: %d\n", + clib_net_to_host_u64 (rmp->pit_entries_count), + clib_net_to_host_u64 (rmp->cs_entries_count), + clib_net_to_host_u64 (rmp->pkts_processed), + clib_net_to_host_u64 (rmp->pkts_interest_count), + clib_net_to_host_u64 (rmp->pkts_data_count), + clib_net_to_host_u64 (rmp->pkts_from_cache_count), + clib_net_to_host_u64 (rmp->pkts_no_pit_count), + clib_net_to_host_u64 (rmp->pit_expired_count), + clib_net_to_host_u64 (rmp->cs_expired_count), + clib_net_to_host_u64 (rmp->cs_lru_count), + clib_net_to_host_u64 (rmp->pkts_drop_no_buf), + clib_net_to_host_u64 (rmp->interests_aggregated), + clib_net_to_host_u64 (rmp->interests_retx)); + } +} + +static int +api_hicn_api_face_ip_add (vat_main_t * vam) +{ + unformat_input_t *input = vam->input; + ip46_address_t nh_addr; + vl_api_hicn_api_face_ip_add_t *mp; + int swif, ret; + + /* Parse args required to build the message */ + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "add %d %U", + &swif, unformat_ip46_address, &nh_addr)) + {; + } + else + { + break; + } + } + + /* Check for presence of both addresses */ + if ((nh_addr.as_u64[0] == (u64) 0) && (nh_addr.as_u64[1] == (u64) 0)) + { + clib_warning ("Next hop address not specified"); + return (1); + } + /* Construct the API message */ + M (HICN_API_FACE_IP_ADD, mp); + mp->nh_addr[0] = clib_host_to_net_u64 (nh_addr.as_u64[0]); + mp->nh_addr[1] = clib_host_to_net_u64 (nh_addr.as_u64[0]); + mp->swif = clib_host_to_net_u32 (swif); + + /* send it... */ + S (mp); + + /* Wait for a reply... */ + W (ret); + + return ret; +} + +static void + vl_api_hicn_api_face_ip_add_reply_t_handler + (vl_api_hicn_api_face_ip_add_reply_t * rmp) +{ + vat_main_t *vam = hicn_test_main.vat_main; + i32 retval = ntohl (rmp->retval); + + if (vam->async_mode) + { + vam->async_errors += (retval < 0); + return; + } + vam->retval = retval; + vam->result_ready = 1; + + if (vam->retval < 0) + { + //vpp_api_test infra will also print out string form of error + fformat (vam->ofp, " (API call error: %d)\n", vam->retval); + return; + } + fformat (vam->ofp, "New Face ID: %d\n", ntohl (rmp->faceid)); +} + +static int +api_hicn_api_face_ip_del (vat_main_t * vam) +{ + unformat_input_t *input = vam->input; + vl_api_hicn_api_face_ip_del_t *mp; + int faceid = 0, ret; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "face %d", &faceid)) + {; + } + else + { + break; + } + } + + //Check for presence of face ID + if (faceid == 0) + { + clib_warning ("Please specify face ID"); + return 1; + } + //Construct the API message + M (HICN_API_FACE_IP_DEL, mp); + mp->faceid = clib_host_to_net_i32 (faceid); + + //send it... + S (mp); + + //Wait for a reply... + W (ret); + + return ret; +} + +static int +api_hicn_api_face_ip_params_get (vat_main_t * vam) +{ + unformat_input_t *input = vam->input; + vl_api_hicn_api_face_ip_params_get_t *mp; + int faceid = 0, ret; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "face %d", &faceid)) + {; + } + else + { + break; + } + } + + //Check for presence of face ID + if (faceid == 0) + { + clib_warning ("Please specify face ID"); + return 1; + } + //Construct the API message + M (HICN_API_FACE_IP_PARAMS_GET, mp); + mp->faceid = clib_host_to_net_i32 (faceid); + + //send it... + S (mp); + + //Wait for a reply... + W (ret); + + return ret; +} + +static void + vl_api_hicn_api_face_ip_params_get_reply_t_handler + (vl_api_hicn_api_face_ip_params_get_reply_t * rmp) +{ + vat_main_t *vam = hicn_test_main.vat_main; + i32 retval = ntohl (rmp->retval); + u8 *sbuf = 0; + u64 nh_addr[2]; + + if (vam->async_mode) + { + vam->async_errors += (retval < 0); + return; + } + vam->retval = retval; + vam->result_ready = 1; + + if (vam->retval < 0) + { + //vpp_api_test infra will also print out string form of error + fformat (vam->ofp, " (API call error: %d)\n", vam->retval); + return; + } + vec_reset_length (sbuf); + nh_addr[0] = clib_net_to_host_u64 (rmp->nh_addr[0]); + nh_addr[1] = clib_net_to_host_u64 (rmp->nh_addr[1]); + sbuf = + format (sbuf, "%U", format_ip46_address, &nh_addr, + 0 /* IP46_ANY_TYPE */ ); + + fformat (vam->ofp, "nh_addr %s swif %d flags %d\n", + sbuf, + clib_net_to_host_u16 (rmp->swif), + clib_net_to_host_i32 (rmp->flags)); +} + +static int +api_hicn_api_route_get (vat_main_t * vam) +{ + unformat_input_t *input = vam->input; + + vl_api_hicn_api_route_get_t *mp; + ip46_address_t prefix; + u8 plen; + int ret; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "prefix %U/%d", unformat_ip46_address, + &prefix, IP46_TYPE_ANY, &plen)) + {; + } + else + { + break; + } + } + + /* Check parse */ + if (((prefix.as_u64[0] == 0) && (prefix.as_u64[1] == 0)) || (plen == 0)) + { + clib_warning ("Please specify a valid prefix..."); + return 1; + } + //Construct the API message + M (HICN_API_ROUTE_GET, mp); + mp->prefix[0] = clib_host_to_net_u64 (((u64 *) & prefix)[0]); + mp->prefix[1] = clib_host_to_net_u64 (((u64 *) & prefix)[1]); + mp->len = plen; + + //send it... + S (mp); + + //Wait for a reply... + W (ret); + + return ret; +} + +static void +vl_api_hicn_api_route_get_reply_t_handler (vl_api_hicn_api_route_get_reply_t * + rmp) +{ + vat_main_t *vam = hicn_test_main.vat_main; + i32 retval = ntohl (rmp->retval); + u8 *sbuf = 0; + + if (vam->async_mode) + { + vam->async_errors += (retval < 0); + return; + } + vam->retval = retval; + vam->result_ready = 1; + + if (vam->retval < 0) + { + //vpp_api_test infra will also print out string form of error + fformat (vam->ofp, " (API call error: %d)\n", vam->retval); + return; + } + int i = 0; + u8 null_face = 0; + u32 faceid; + + vec_reset_length (sbuf); + sbuf = format (sbuf, "Faces: \n"); + while (i < 1000 && !null_face) + { + faceid = clib_net_to_host_u32 (rmp->faceids[i]); + if (faceid != HICN_FACE_NULL) + { + sbuf = + format (sbuf, "faceid %d", + clib_net_to_host_u32 (rmp->faceids[i])); + i++; + } + else + { + null_face = 1; + } + } + + fformat (vam->ofp, "%s\n Strategy: %d", + sbuf, clib_net_to_host_u32 (rmp->strategy_id)); +} + +static int +api_hicn_api_route_nhops_add (vat_main_t * vam) +{ + unformat_input_t *input = vam->input; + vl_api_hicn_api_route_nhops_add_t *mp; + + ip46_address_t prefix; + u8 plen; + u32 faceid = 0; + int ret; + + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "add prefix %U/%d", unformat_ip46_address, + &prefix, IP46_TYPE_ANY, &plen)) + {; + } + else if (unformat (input, "face %d", &faceid)) + {; + } + else + { + break; + } + } + + /* Check parse */ + if (((prefix.as_u64[0] == 0) && (prefix.as_u64[1] == 0)) || (plen == 0) + || (faceid == 0)) + { + clib_warning ("Please specify prefix and faceid..."); + return 1; + } + /* Construct the API message */ + M (HICN_API_ROUTE_NHOPS_ADD, mp); + mp->prefix[0] = clib_host_to_net_u64 (((u64 *) & prefix)[0]); + mp->prefix[1] = clib_host_to_net_u64 (((u64 *) & prefix)[1]); + mp->len = plen; + + mp->face_ids[0] = clib_host_to_net_u32 (faceid); + mp->n_faces = 1; + + /* send it... */ + S (mp); + + /* Wait for a reply... */ + W (ret); + + return ret; +} + +static int +api_hicn_api_route_del (vat_main_t * vam) +{ + unformat_input_t *input = vam->input; + vl_api_hicn_api_route_del_t *mp; + + ip46_address_t prefix; + u8 plen; + int ret; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "prefix %U/%d", unformat_ip46_address, + &prefix, IP46_TYPE_ANY, &plen)) + {; + } + else + { + break; + } + } + + /* Check parse */ + if (((prefix.as_u64[0] == 0) && (prefix.as_u64[1] == 0)) || (plen == 0)) + { + clib_warning ("Please specify prefix..."); + return 1; + } + /* Construct the API message */ + M (HICN_API_ROUTE_DEL, mp); + mp->prefix[0] = clib_host_to_net_u64 (((u64 *) & prefix)[0]); + mp->prefix[1] = clib_host_to_net_u64 (((u64 *) & prefix)[1]); + mp->len = plen; + + /* send it... */ + S (mp); + + /* Wait for a reply... */ + W (ret); + + return ret; + +} + +static int +api_hicn_api_route_nhop_del (vat_main_t * vam) +{ + unformat_input_t *input = vam->input; + vl_api_hicn_api_route_nhop_del_t *mp; + + ip46_address_t prefix; + u8 plen; + int faceid = 0, ret; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "del prefix %U/%d", unformat_ip46_address, + &prefix, IP46_TYPE_ANY, &plen)) + {; + } + else if (unformat (input, "face %d", &faceid)) + {; + } + else + { + break; + } + } + + /* Check parse */ + if (((prefix.as_u64[0] == 0) && (prefix.as_u64[1] == 0)) || (plen == 0) + || (faceid == HICN_FACE_NULL)) + { + clib_warning ("Please specify prefix and faceid..."); + return 1; + } + /* Construct the API message */ + M (HICN_API_ROUTE_NHOP_DEL, mp); + mp->prefix[0] = clib_host_to_net_u64 (((u64 *) & prefix)[0]); + mp->prefix[1] = clib_host_to_net_u64 (((u64 *) & prefix)[1]); + mp->len = plen; + + mp->faceid = clib_host_to_net_u32 (faceid); + + /* send it... */ + S (mp); + + /* Wait for a reply... */ + W (ret); + + return ret; +} + +static int +api_hicn_api_strategies_get (vat_main_t * vam) +{ + vl_api_hicn_api_strategies_get_t *mp; + int ret; + + //TODO + /* Construct the API message */ + M (HICN_API_STRATEGIES_GET, mp); + + /* send it... */ + S (mp); + + /* Wait for a reply... */ + W (ret); + + return ret; +} + +static void + vl_api_hicn_api_strategies_get_reply_t_handler + (vl_api_hicn_api_strategies_get_reply_t * mp) +{ + vat_main_t *vam = hicn_test_main.vat_main; + i32 retval = ntohl (mp->retval); + u8 *sbuf = 0; + + if (vam->async_mode) + { + vam->async_errors += (retval < 0); + return; + } + vam->retval = retval; + vam->result_ready = 1; + + if (vam->retval < 0) + { + //vpp_api_test infra will also print out string form of error + fformat (vam->ofp, " (API call error: %d)\n", vam->retval); + return; + } + int n_strategies = clib_net_to_host_i32 (mp->n_strategies); + + vec_reset_length (sbuf); + sbuf = format (sbuf, "Available strategies:\n"); + + int i; + for (i = 0; i < n_strategies; i++) + { + u32 strategy_id = clib_net_to_host_u32 (mp->strategy_id[i]); + sbuf = format (sbuf, "%d ", strategy_id); + } + fformat (vam->ofp, "%s", sbuf); +} + +static int +api_hicn_api_strategy_get (vat_main_t * vam) +{ + unformat_input_t *input = vam->input; + vl_api_hicn_api_strategy_get_t *mp; + int ret; + + u32 strategy_id = HICN_STRATEGY_NULL; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "strategy %d", strategy_id)) + {; + } + else + { + break; + } + } + + if (strategy_id == HICN_STRATEGY_NULL) + { + clib_warning ("Please specify strategy id..."); + return 1; + } + + /* Construct the API message */ + M (HICN_API_STRATEGY_GET, mp); + mp->strategy_id = clib_host_to_net_u32 (strategy_id); + + /* send it... */ + S (mp); + + /* Wait for a reply... */ + W (ret); + + return ret; +} + +static void + vl_api_hicn_api_strategy_get_reply_t_handler + (vl_api_hicn_api_strategy_get_reply_t * mp) +{ + vat_main_t *vam = hicn_test_main.vat_main; + i32 retval = ntohl (mp->retval); + + if (vam->async_mode) + { + vam->async_errors += (retval < 0); + return; + } + vam->retval = retval; + vam->result_ready = 1; + + if (vam->retval < 0) + { + //vpp_api_test infra will also print out string form of error + fformat (vam->ofp, " (API call error: %d)\n", vam->retval); + return; + } + fformat (vam->ofp, "%s", mp->description); +} + +static int +api_hicn_api_register_prod_app (vat_main_t * vam) +{ + unformat_input_t *input = vam->input; + vl_api_hicn_api_register_prod_app_t *mp; + ip46_address_t prefix; + int plen; + u32 swif = ~0; + int ret; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "prefix %U/%d", unformat_ip46_address, + &prefix, IP46_TYPE_ANY, &plen)) + {; + } + else if (unformat (input, "id %d", &swif)) + {; + } + else + { + break; + } + } + + /* Check parse */ + if (((prefix.as_u64[0] == 0) && (prefix.as_u64[1] == 0)) || (plen == 0)) + { + clib_warning ("Please specify prefix..."); + return 1; + } + /* Construct the API message */ + M (HICN_API_REGISTER_PROD_APP, mp); + mp->prefix[0] = clib_host_to_net_u64 (prefix.as_u64[0]); + mp->prefix[1] = clib_host_to_net_u64 (prefix.as_u64[1]); + mp->len = (u8) plen; + + mp->swif = clib_host_to_net_u32 (swif); + + /* send it... */ + S (mp); + + /* Wait for a reply... */ + W (ret); + + return ret; +} + +static void + vl_api_hicn_api_register_prod_app_reply_t_handler + (vl_api_hicn_api_register_prod_app_reply_t * mp) +{ + vat_main_t *vam = hicn_test_main.vat_main; + i32 retval = ntohl (mp->retval); + + if (vam->async_mode) + { + vam->async_errors += (retval < 0); + return; + } + vam->retval = retval; + vam->result_ready = 1; + + if (vam->retval < 0) + { + //vpp_api_test infra will also print out string form of error + fformat (vam->ofp, " (API call error: %d)\n", vam->retval); + return; + } +} + +static int +api_hicn_api_register_cons_app (vat_main_t * vam) +{ + vl_api_hicn_api_register_cons_app_t *mp; + int ret; + + /* Construct the API message */ + M (HICN_API_REGISTER_CONS_APP, mp); + + /* send it... */ + S (mp); + + /* Wait for a reply... */ + W (ret); + + return ret; +} + +static void + vl_api_hicn_api_register_cons_app_reply_t_handler + (vl_api_hicn_api_register_cons_app_reply_t * mp) +{ + vat_main_t *vam = hicn_test_main.vat_main; + i32 retval = ntohl (mp->retval); + + if (vam->async_mode) + { + vam->async_errors += (retval < 0); + return; + } + vam->retval = retval; + vam->result_ready = 1; + + if (vam->retval < 0) + { + //vpp_api_test infra will also print out string form of error + fformat (vam->ofp, " (API call error: %d)\n", vam->retval); + return; + } + ip4_address_t src_addr4; + src_addr4.as_u32 = clib_net_to_host_u32 (mp->src_addr4); + ip6_address_t src_addr6; + src_addr6.as_u64[0] = clib_net_to_host_u64 (mp->src_addr6[0]); + src_addr6.as_u64[1] = clib_net_to_host_u64 (mp->src_addr6[1]); + + fformat (vam->ofp, + "ip4 address %U\n" + "ip6 address :%U\n" + "appif id :%d\n", + format_ip4_address, &src_addr4, format_ip6_address, &src_addr6); +} + +/* + * List of messages that the api test plugin sends, and that the data plane + * plugin processes + */ +#define foreach_vpe_api_msg \ +_(hicn_api_node_params_set, "PIT size CS size " \ + "PIT minlimit PIT maxlimit [disable] ") \ +_(hicn_api_node_params_get, "") \ +_(hicn_api_node_stats_get, "") \ +_(hicn_api_face_ip_del, "face ") \ +_(hicn_api_face_ip_add, "add
") \ +_(hicn_api_route_nhops_add, "add prefix / face weight ") \ +_(hicn_api_face_ip_params_get, "face ") \ +_(hicn_api_route_get, "prefix /") \ +_(hicn_api_route_del, "prefix /") \ +_(hicn_api_route_nhop_del, "del prefix / face ") \ +_(hicn_api_strategies_get, "") \ +_(hicn_api_strategy_get, "strategy ") \ +_(hicn_api_register_prod_app, "prefix / id ") \ +_(hicn_api_register_cons_app, "") + +void +hicn_vat_api_hookup (vat_main_t * vam) +{ + hicn_test_main_t *sm = &hicn_test_main; + /* Hook up handlers for replies from the data plane plug-in */ +#define _(N, n) \ + vl_msg_api_set_handlers((VL_API_##N + sm->msg_id_base), \ + #n, \ + vl_api_##n##_t_handler, \ + vl_noop_handler, \ + vl_api_##n##_t_endian, \ + vl_api_##n##_t_print, \ + sizeof(vl_api_##n##_t), 1); + foreach_vpe_api_reply_msg; +#undef _ + + /* API messages we can send */ +#define _(n, h) hash_set_mem (vam->function_by_name, #n, api_##n); + foreach_vpe_api_msg; +#undef _ + + /* Help strings */ +#define _(n, h) hash_set_mem (vam->help_by_name, #n, h); + foreach_vpe_api_msg; +#undef _ +} + +clib_error_t * +vat_plugin_register (vat_main_t * vam) +{ + hicn_test_main_t *sm = &hicn_test_main; + u8 *name; + + sm->vat_main = vam; + + /* Ask the vpp engine for the first assigned message-id */ + name = format (0, "hicn_%08x%c", api_version, 0); + sm->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name); + + if (sm->msg_id_base != (u16) ~ 0) + hicn_vat_api_hookup (vam); + + vec_free (name); + + return 0; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/hicn_msg_enum.h b/hicn-plugin/src/hicn_msg_enum.h new file mode 100755 index 000000000..291e6226c --- /dev/null +++ b/hicn-plugin/src/hicn_msg_enum.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_MSG_ENUM_H__ +#define __HICN_MSG_ENUM_H__ + +#include + +#define vl_msg_id(n, h) n, +typedef enum +{ +#include + /* We'll want to know how many messages IDs we need... */ + VL_MSG_FIRST_AVAILABLE, +} vl_msg_id_t; +#undef vl_msg_id + +#endif /* __HICN_MSG_ENUM_H__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/infra.h b/hicn-plugin/src/infra.h new file mode 100755 index 000000000..a9744fe97 --- /dev/null +++ b/hicn-plugin/src/infra.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_INFRA_H__ +#define __HICN_INFRA_H__ + +#include +#include + +#include "pcs.h" + +/** + * hICN plugin global state: see also + * - fib and pits + */ +typedef struct hicn_main_s +{ + /* Binary API message ID base */ + u16 msg_id_base; + + /* Have we been enabled */ + u16 is_enabled; + + /* Forwarder PIT/CS */ + hicn_pit_cs_t pitcs; + + /* Global PIT lifetime info */ + /* + * Default PIT entry timeout to use in case an interest does not + * contain a valid interest lifetime + */ + u64 pit_lifetime_dflt_ms; + /* + * Boundarier for the interest lifetime. If outside, + * pit_lifetime_dflt_ms is used in the PIT + */ + u64 pit_lifetime_min_ms; + u64 pit_lifetime_max_ms; + +} hicn_main_t; + +extern hicn_main_t hicn_main; + +extern int hicn_infra_fwdr_initialized; + +/* PIT and CS size */ +u32 hicn_infra_pit_size; +u32 hicn_infra_cs_size; + +/** + * @brief Enable and disable the hicn plugin + * + * Enable the time the hICN plugin and set the forwarder parameters. + * @param enable_disable 1 if to enable, 0 otherwisw (currently only enable is supported) + * @param pit_max_size Max size of the PIT + * @param pit_dflt_lifetime_sec_req Default PIT entry timeout to use in case an interest does not contain a valid interest lifetime + * @param pit_min_lifetime_sec_req Minimum timeout allowed for a PIT entry lifetime + * @param pit_max_lifetime_sec_req Maximum timeout allowed for a PIT entry lifetime + * @param cs_max_size CS size. Must be <= than pit_max_size + * @param cs_reserved_app Amount of CS reserved for application faces + */ +int +hicn_infra_plugin_enable_disable (int enable_disable, + int pit_max_size, + f64 pit_dflt_lifetime_sec_req, + f64 pit_min_lifetime_sec_req, + f64 pit_max_lifetime_sec_req, + int cs_max_size, int cs_reserved_app); + + +/* vlib nodes that compose the hICN forwarder */ +extern vlib_node_registration_t hicn_interest_pcslookup_node; +extern vlib_node_registration_t hicn_data_pcslookup_node; +extern vlib_node_registration_t hicn_data_fwd_node; +extern vlib_node_registration_t hicn_data_store_node; +extern vlib_node_registration_t hicn_interest_hitpit_node; +extern vlib_node_registration_t hicn_interest_hitcs_node; +extern vlib_node_registration_t hicn_pg_interest_node; +extern vlib_node_registration_t hicn_pg_data_node; +extern vlib_node_registration_t hicn_pg_server_node; + + +#endif /* // __HICN_INFRA_H__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/interest_hitcs.h b/hicn-plugin/src/interest_hitcs.h new file mode 100755 index 000000000..82b0ace54 --- /dev/null +++ b/hicn-plugin/src/interest_hitcs.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_INTEREST_HITCS_H__ +#define __HICN_INTEREST_HITCS_H__ + +#include +#include + +#include "pcs.h" + +/* + * Node context data; we think this is per-thread/instance + */ +typedef struct hicn_interest_hitcs_runtime_s +{ + int id; + hicn_pit_cs_t *pitcs; +} hicn_interest_hitcs_runtime_t; + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; +} hicn_interest_hitcs_trace_t; + +typedef enum +{ + HICN_INTEREST_HITCS_NEXT_V4_LOOKUP, + HICN_INTEREST_HITCS_NEXT_V6_LOOKUP, + HICN_INTEREST_HITCS_NEXT_ERROR_DROP, + HICN_INTEREST_HITCS_N_NEXT, +} hicn_interest_hitcs_next_t; + +#endif /* // __HICN_INTEREST_HITCS_H__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/interest_hitcs_node.c b/hicn-plugin/src/interest_hitcs_node.c new file mode 100755 index 000000000..f9c8c4898 --- /dev/null +++ b/hicn-plugin/src/interest_hitcs_node.c @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "interest_hitcs.h" +#include "mgmt.h" +#include "parser.h" +#include "data_fwd.h" +#include "infra.h" +#include "state.h" +#include "error.h" + +/* packet trace format function */ +static u8 *hicn_interest_hitcs_format_trace (u8 * s, va_list * args); + + +/* Stats string values */ +static char *hicn_interest_hitcs_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + +vlib_node_registration_t hicn_interest_hitcs_node; + +always_inline void drop_packet (u32 * next0); + +always_inline void +clone_from_cs (vlib_main_t * vm, u32 * bi0_cs, vlib_buffer_t * dest) +{ + /* Retrieve the buffer to clone */ + vlib_buffer_t *cs_buf = vlib_get_buffer (vm, *bi0_cs); + + if (cs_buf->current_data >= VLIB_BUFFER_MIN_CHAIN_SEG_SIZE + && ((i16) cs_buf->current_length) < (i16) 0) + { + vlib_buffer_advance (cs_buf, + -(((i16) cs_buf->current_length) + + VLIB_BUFFER_MIN_CHAIN_SEG_SIZE)); + + clib_memcpy (vlib_buffer_get_current (dest), + vlib_buffer_get_current (cs_buf), cs_buf->current_length); + clib_memcpy (dest->opaque2, cs_buf->opaque2, sizeof (cs_buf->opaque2)); + dest->current_data = cs_buf->current_data; + dest->current_length = cs_buf->current_length; + dest->total_length_not_including_first_buffer = 0; + cs_buf->current_data += VLIB_BUFFER_MIN_CHAIN_SEG_SIZE; + cs_buf->current_length -= VLIB_BUFFER_MIN_CHAIN_SEG_SIZE; + } + else + { + vlib_buffer_advance (cs_buf, -VLIB_BUFFER_MIN_CHAIN_SEG_SIZE); + + if (PREDICT_FALSE (cs_buf->n_add_refs == 255)) + { + vlib_buffer_t *cs_buf2 = vlib_buffer_copy (vm, cs_buf); + vlib_buffer_advance (cs_buf, VLIB_BUFFER_MIN_CHAIN_SEG_SIZE); + cs_buf = cs_buf2; + } + + clib_memcpy (vlib_buffer_get_current (dest), + vlib_buffer_get_current (cs_buf), + VLIB_BUFFER_MIN_CHAIN_SEG_SIZE); + dest->current_length = VLIB_BUFFER_MIN_CHAIN_SEG_SIZE; + vlib_buffer_advance (cs_buf, VLIB_BUFFER_MIN_CHAIN_SEG_SIZE); + vlib_buffer_attach_clone (vm, dest, cs_buf); + } +} + +/* + * ICN forwarder node for interests: handling of Interests delivered based on + * ACL. - 1 packet at a time - ipv4/tcp ipv6/tcp + */ +static uword +hicn_interest_hitcs_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next; + hicn_interest_hitcs_next_t next_index; + hicn_interest_hitcs_runtime_t *rt; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + f64 tnow; + int ret; + + rt = vlib_node_get_runtime_data (vm, hicn_interest_hitcs_node.index); + + if (PREDICT_FALSE (rt->pitcs == NULL)) + { + rt->pitcs = &hicn_main.pitcs; + } + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + /* Capture time in vpp terms */ + tnow = vlib_time_now (vm); + while (n_left_from > 0) + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + while (n_left_from > 0 && n_left_to_next > 0) + { + vlib_buffer_t *b0; + u8 isv6; + u8 *nameptr; + u16 namelen; + u32 bi0; + u32 next0 = HICN_INTEREST_HITCS_NEXT_ERROR_DROP; + hicn_name_t name; + hicn_header_t *hicn0; + hicn_buffer_t *hicnb0; + hicn_hash_node_t *node0; + hicn_pcs_entry_t *pitp; + hicn_hash_entry_t *hash_entry0; + const hicn_strategy_vft_t *strategy_vft0; + const hicn_dpo_vft_t *dpo_vft0; + u8 dpo_ctx_id0; + + /* Prefetch for next iteration. */ + if (n_left_from > 1) + { + vlib_buffer_t *b1; + b1 = vlib_get_buffer (vm, from[1]); + CLIB_PREFETCH (b1, 2 * CLIB_CACHE_LINE_BYTES, STORE); + CLIB_PREFETCH (b1->data, CLIB_CACHE_LINE_BYTES, STORE); + } + + /* Dequeue a packet buffer */ + bi0 = from[0]; + from += 1; + n_left_from -= 1; + to_next[0] = bi0; + to_next += 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + + /* Get hicn buffer and state */ + hicnb0 = hicn_get_buffer (b0); + hicn_get_internal_state (hicnb0, rt->pitcs, &node0, &strategy_vft0, + &dpo_vft0, &dpo_ctx_id0, &hash_entry0); + + ret = hicn_interest_parse_pkt (b0, &name, &namelen, &hicn0, &isv6); + nameptr = (u8 *) (&name); + pitp = hicn_pit_get_data (node0); + + dpo_id_t hicn_dpo_id0 = + { dpo_vft0->hicn_dpo_get_type (), 0, 0, dpo_ctx_id0 }; + + if (PREDICT_FALSE + (ret != HICN_ERROR_NONE || + !hicn_node_compare (nameptr, namelen, node0))) + { + /* Remove lock from the entry */ + hicn_pcs_remove_lock (rt->pitcs, &pitp, &node0, vm, hash_entry0, + dpo_vft0, &hicn_dpo_id0); + drop_packet (&next0); + goto end_processing; + } + if ((tnow > pitp->shared.expire_time)) + { + /* Delete and clean up expired CS entry */ + hicn_pcs_delete (rt->pitcs, &pitp, &node0, vm, hash_entry0, + dpo_vft0, &hicn_dpo_id0); + stats.cs_expired_count++; + /* Forward interest to the strategy node */ + next0 = + isv6 ? HICN_INTEREST_HITCS_NEXT_V6_LOOKUP : + HICN_INTEREST_HITCS_NEXT_V4_LOOKUP; + } + else + { + if (PREDICT_TRUE + (!(hash_entry0->he_flags & HICN_HASH_ENTRY_FLAG_DELETED))) + hicn_pcs_cs_update (vm, rt->pitcs, pitp, node0); + + /* + * Retrieve the incoming iface and forward + * the data through it + */ + ASSERT (hicnb0->face_dpo_id.dpoi_index < + HICN_PARAM_PIT_ENTRY_PHOPS_MAX); + next0 = hicnb0->face_dpo_id.dpoi_next_node; + vnet_buffer (b0)->ip.adj_index[VLIB_TX] = + hicnb0->face_dpo_id.dpoi_index; + + clone_from_cs (vm, &pitp->u.cs.cs_pkt_buf, b0); + + stats.pkts_from_cache_count++; + stats.pkts_data_count++; + /* Remove lock from the entry */ + hicn_pcs_remove_lock (rt->pitcs, &pitp, &node0, vm, hash_entry0, + dpo_vft0, &hicn_dpo_id0); + } + + end_processing: + + /* Maybe trace */ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + hicn_interest_hitcs_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->pkt_type = HICN_PKT_TYPE_INTEREST; + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + t->next_index = next0; + } + /* Incr packet counter */ + stats.pkts_processed += 1; + + /* + * Verify speculative enqueue, maybe switch current + * next frame + */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + u32 pit_int_count = hicn_pit_get_int_count (rt->pitcs); + + vlib_node_increment_counter (vm, hicn_interest_hitcs_node.index, + HICNFWD_ERROR_CACHED, + stats.pkts_from_cache_count); + + vlib_node_increment_counter (vm, hicn_interest_hitcs_node.index, + HICNFWD_ERROR_DATAS, stats.pkts_data_count); + + update_node_counter (vm, hicn_interest_hitcs_node.index, + HICNFWD_ERROR_INT_COUNT, pit_int_count); + + return (frame->n_vectors); +} + +always_inline void +drop_packet (u32 * next0) +{ + *next0 = HICN_INTEREST_HITCS_NEXT_ERROR_DROP; +} + +/* packet trace format function */ +static u8 * +hicn_interest_hitcs_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_interest_hitcs_trace_t *t = + va_arg (*args, hicn_interest_hitcs_trace_t *); + + s = format (s, "INTEREST-HITCS: pkt: %d, sw_if_index %d, next index %d", + (int) t->pkt_type, t->sw_if_index, t->next_index); + return (s); +} + +/* + * Node registration for the interest forwarder node + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE(hicn_interest_hitcs_node) = +{ + .function = hicn_interest_hitcs_node_fn, + .name = "hicn-interest-hitcs", + .vector_size = sizeof(u32), + .runtime_data_bytes = sizeof(hicn_interest_hitcs_runtime_t), + .format_trace = hicn_interest_hitcs_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(hicn_interest_hitcs_error_strings), + .error_strings = hicn_interest_hitcs_error_strings, + .n_next_nodes = HICN_INTEREST_HITCS_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = + { + [HICN_INTEREST_HITCS_NEXT_V4_LOOKUP] = "ip4-lookup", + [HICN_INTEREST_HITCS_NEXT_V6_LOOKUP] = "ip6-lookup", + [HICN_INTEREST_HITCS_NEXT_ERROR_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/interest_hitpit.h b/hicn-plugin/src/interest_hitpit.h new file mode 100755 index 000000000..28427d342 --- /dev/null +++ b/hicn-plugin/src/interest_hitpit.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_INTEREST_HITPIT_H__ +#define __HICN_INTEREST_HITPIT_H__ + +#include +#include + +#include "pcs.h" + +/* + * Node context data; we think this is per-thread/instance + */ +typedef struct hicn_interest_hitpit_runtime_s +{ + int id; + hicn_pit_cs_t *pitcs; +} hicn_interest_hitpit_runtime_t; + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; +} hicn_interest_hitpit_trace_t; + +typedef enum +{ + HICN_INTEREST_HITPIT_NEXT_INTEREST_HITCS, + HICN_INTEREST_HITPIT_NEXT_IP4_LOOKUP, + HICN_INTEREST_HITPIT_NEXT_IP6_LOOKUP, + HICN_INTEREST_HITPIT_NEXT_ERROR_DROP, + HICN_INTEREST_HITPIT_N_NEXT, +} hicn_interest_hitpit_next_t; + +#endif /* // __HICN_INTEREST_HITPIT_H__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/interest_hitpit_node.c b/hicn-plugin/src/interest_hitpit_node.c new file mode 100755 index 000000000..21ba97db3 --- /dev/null +++ b/hicn-plugin/src/interest_hitpit_node.c @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "interest_hitpit.h" +#include "mgmt.h" +#include "parser.h" +#include "data_fwd.h" +#include "infra.h" +#include "strategy.h" +#include "strategy_dpo_ctx.h" +#include "strategy_dpo_manager.h" +#include "state.h" +#include "error.h" +#include "face_db.h" + +/* packet trace format function */ +static u8 *hicn_interest_hitpit_format_trace (u8 * s, va_list * args); + +/* Stats string values */ +static char *hicn_interest_hitpit_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + +vlib_node_registration_t hicn_interest_hitpit_node; + +always_inline void drop_packet (u32 * next0); + +/* + * hICN forwarder node for interests hitting the PIT + */ +static uword +hicn_interest_hitpit_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next; + hicn_interest_hitpit_next_t next_index; + hicn_interest_hitpit_runtime_t *rt; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + f64 tnow; + + rt = vlib_node_get_runtime_data (vm, hicn_interest_hitpit_node.index); + + if (PREDICT_FALSE (rt->pitcs == NULL)) + { + rt->pitcs = &hicn_main.pitcs; + } + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + /* Capture time in vpp terms */ + tnow = vlib_time_now (vm); + + while (n_left_from > 0) + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + while (n_left_from > 0 && n_left_to_next > 0) + { + vlib_buffer_t *b0; + u8 isv6; + u8 *nameptr; + u16 namelen; + u32 bi0; + u32 next0 = HICN_INTEREST_HITPIT_NEXT_ERROR_DROP; + hicn_name_t name; + hicn_header_t *hicn0; + hicn_hash_node_t *node0; + const hicn_strategy_vft_t *strategy_vft0; + const hicn_dpo_vft_t *dpo_vft0; + hicn_pcs_entry_t *pitp; + u8 dpo_ctx_id0; + u8 found = 0; + int nh_idx; + dpo_id_t *outface; + hicn_hash_entry_t *hash_entry0; + hicn_buffer_t *hicnb0; + int ret; + + /* Prefetch for next iteration. */ + if (n_left_from > 1) + { + vlib_buffer_t *b1; + b1 = vlib_get_buffer (vm, from[1]); + CLIB_PREFETCH (b1, CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH (b1->data, CLIB_CACHE_LINE_BYTES, STORE); + } + + /* Dequeue a packet buffer */ + bi0 = from[0]; + from += 1; + n_left_from -= 1; + to_next[0] = bi0; + to_next += 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + + /* Get hicn buffer and state */ + hicnb0 = hicn_get_buffer (b0); + hicn_get_internal_state (hicnb0, rt->pitcs, &node0, &strategy_vft0, + &dpo_vft0, &dpo_ctx_id0, &hash_entry0); + + + ret = hicn_interest_parse_pkt (b0, &name, &namelen, &hicn0, &isv6); + nameptr = (u8 *) (&name); + pitp = hicn_pit_get_data (node0); + dpo_id_t hicn_dpo_id0 = + { dpo_vft0->hicn_dpo_get_type (), 0, 0, dpo_ctx_id0 }; + + /* + * Check if the hit is instead a collision in the + * hash table. Unlikely to happen. + */ + if (PREDICT_FALSE + (ret != HICN_ERROR_NONE + || !hicn_node_compare (nameptr, namelen, node0))) + { + stats.interests_hash_collision++; + /* Remove lock from the entry */ + hicn_pcs_remove_lock (rt->pitcs, &pitp, &node0, vm, hash_entry0, + dpo_vft0, &hicn_dpo_id0); + drop_packet (&next0); + + goto end_processing; + } + /* + * If the entry is expired, remove it no matter of + * the possible cases. + */ + if (tnow > pitp->shared.expire_time) + { + strategy_vft0->hicn_on_interest_timeout (dpo_ctx_id0); + hicn_pcs_delete (rt->pitcs, &pitp, &node0, vm, hash_entry0, + dpo_vft0, &hicn_dpo_id0); + stats.pit_expired_count++; + next0 = + isv6 ? HICN_INTEREST_HITPIT_NEXT_IP6_LOOKUP : + HICN_INTEREST_HITPIT_NEXT_IP4_LOOKUP; + } + else + { + if ((hash_entry0->he_flags & HICN_HASH_ENTRY_FLAG_CS_ENTRY)) + { + next0 = HICN_INTEREST_HITPIT_NEXT_INTEREST_HITCS; + } + else + { + /* + * Distinguish between aggregation or + * retransmission + */ + + found = + hicn_face_search (&(hicnb0->face_dpo_id), + &(pitp->u.pit.faces)); + + if (found) + { + /* + * Remove lock on the dpo + * stored in the vlib_buffer + */ + dpo_unlock (&hicnb0->face_dpo_id); + strategy_vft0->hicn_select_next_hop (dpo_ctx_id0, + &nh_idx, &outface); + /* Retransmission */ + /* + * Prepare the packet for the + * forwarding + */ + next0 = outface->dpoi_next_node; + vnet_buffer (b0)->ip.adj_index[VLIB_TX] = + outface->dpoi_index; + + /* + * Update the egress face in + * the PIT + */ + pitp->u.pit.pe_txnh = nh_idx; + stats.interests_retx++; + } + else + { + hicn_face_db_add_face_dpo (&hicnb0->face_dpo_id, + &pitp->u.pit.faces); + + /* Aggregation */ + drop_packet (&next0); + stats.interests_aggregated++; + } + /* Remove lock from the entry */ + hicn_pcs_remove_lock (rt->pitcs, &pitp, &node0, vm, + hash_entry0, dpo_vft0, &hicn_dpo_id0); + + } + } + end_processing: + + /* Maybe trace */ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + hicn_interest_hitpit_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->pkt_type = HICN_PKT_TYPE_INTEREST; + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + t->next_index = next0; + } + /* Incr packet counter */ + stats.pkts_processed += 1; + + /* + * Verify speculative enqueue, maybe switch current + * next frame + */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + u32 pit_int_count = hicn_pit_get_int_count (rt->pitcs); + + + vlib_node_increment_counter (vm, hicn_interest_hitpit_node.index, + HICNFWD_ERROR_PROCESSED, stats.pkts_processed); + vlib_node_increment_counter (vm, hicn_interest_hitpit_node.index, + HICNFWD_ERROR_INTEREST_AGG, + stats.interests_aggregated); + vlib_node_increment_counter (vm, hicn_interest_hitpit_node.index, + HICNFWD_ERROR_INT_RETRANS, + stats.interests_retx); + vlib_node_increment_counter (vm, hicn_interest_hitpit_node.index, + HICNFWD_ERROR_PIT_EXPIRED, + stats.pit_expired_count); + vlib_node_increment_counter (vm, hicn_interest_hitpit_node.index, + HICNFWD_ERROR_HASH_COLL_HASHTB_COUNT, + stats.interests_hash_collision); + + update_node_counter (vm, hicn_interest_hitpit_node.index, + HICNFWD_ERROR_INT_COUNT, pit_int_count); + + return (frame->n_vectors); +} + +/* packet trace format function */ +static u8 * +hicn_interest_hitpit_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_interest_hitpit_trace_t *t = + va_arg (*args, hicn_interest_hitpit_trace_t *); + + s = format (s, "INTEREST-HITPIT: pkt: %d, sw_if_index %d, next index %d", + (int) t->pkt_type, t->sw_if_index, t->next_index); + return (s); +} + +void +drop_packet (u32 * next0) +{ + *next0 = HICN_INTEREST_HITPIT_NEXT_ERROR_DROP; +} + +/* + * Node registration for the interest forwarder node + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE(hicn_interest_hitpit_node) = +{ + .function = hicn_interest_hitpit_node_fn, + .name = "hicn-interest-hitpit", + .vector_size = sizeof(u32), + .runtime_data_bytes = sizeof(hicn_interest_hitpit_runtime_t), + .format_trace = hicn_interest_hitpit_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(hicn_interest_hitpit_error_strings), + .error_strings = hicn_interest_hitpit_error_strings, + .n_next_nodes = HICN_INTEREST_HITPIT_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = + { + [HICN_INTEREST_HITPIT_NEXT_INTEREST_HITCS] = "hicn-interest-hitcs", + [HICN_INTEREST_HITPIT_NEXT_IP4_LOOKUP] = "ip4-lookup", + [HICN_INTEREST_HITPIT_NEXT_IP6_LOOKUP] = "ip6-lookup", + [HICN_INTEREST_HITPIT_NEXT_ERROR_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/interest_pcslookup.h b/hicn-plugin/src/interest_pcslookup.h new file mode 100755 index 000000000..e27673a9e --- /dev/null +++ b/hicn-plugin/src/interest_pcslookup.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_INTEREST_PCSLOOKUP_H__ +#define __HICN_INTEREST_PCSLOOKUP_H__ + +#include +#include + +#include "pcs.h" + +/* + * Node context data; we think this is per-thread/instance + */ +typedef struct hicn_interest_pcslookup_runtime_s +{ + int id; + hicn_pit_cs_t *pitcs; +} hicn_interest_pcslookup_runtime_t; + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; +} hicn_interest_pcslookup_trace_t; + +typedef enum +{ + HICN_INTEREST_PCSLOOKUP_NEXT_V4_LOOKUP, + HICN_INTEREST_PCSLOOKUP_NEXT_V6_LOOKUP, + HICN_INTEREST_PCSLOOKUP_NEXT_INTEREST_HITPIT, + HICN_INTEREST_PCSLOOKUP_NEXT_INTEREST_HITCS, + HICN_INTEREST_PCSLOOKUP_NEXT_ERROR_DROP, + HICN_INTEREST_PCSLOOKUP_N_NEXT, +} hicn_interest_pcslookup_next_t; + +#endif /* // __HICN_INTEREST_PCSLOOKUP_H__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/interest_pcslookup_node.c b/hicn-plugin/src/interest_pcslookup_node.c new file mode 100755 index 000000000..40d62510b --- /dev/null +++ b/hicn-plugin/src/interest_pcslookup_node.c @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "interest_pcslookup.h" +#include "mgmt.h" +#include "parser.h" +#include "infra.h" +#include "strategy_dpo_manager.h" +#include "error.h" +#include "state.h" + +/** + * @FILE This node performs a lookup in the PIT and CS for a received interest packet. + * + * This node passes the packet to the interest-hitpit and interest-hitcs nodes + * when there is a hit in the pit or content store, respectively. + */ + +/* Functions declarations */ + +/* packet trace format function */ +static u8 *hicn_interest_pcslookup_format_trace (u8 * s, va_list * args); + + +/* Stats string values */ +static char *hicn_interest_pcslookup_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + +vlib_node_registration_t hicn_interest_pcslookup_node; + +/* + * ICN forwarder node for interests: handling of Interests delivered based on + * ACL. - 1 packet at a time - ipv4/tcp ipv6/tcp + */ +static uword +hicn_interest_pcslookup_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next; + hicn_interest_pcslookup_next_t next_index; + hicn_interest_pcslookup_runtime_t *rt; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + int ret; + + rt = vlib_node_get_runtime_data (vm, hicn_interest_pcslookup_node.index); + + if (PREDICT_FALSE (rt->pitcs == NULL)) + { + rt->pitcs = &hicn_main.pitcs; + } + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + while (n_left_from > 0 && n_left_to_next > 0) + { + vlib_buffer_t *b0; + u8 isv6; + u8 *nameptr; + u16 namelen; + u32 bi0; + u32 next0 = HICN_INTEREST_PCSLOOKUP_NEXT_ERROR_DROP; + u64 name_hash = 0; + hicn_name_t name; + hicn_header_t *hicn0; + u32 node_id0 = 0; + u8 dpo_ctx_id0 = 0; + u8 vft_id0 = 0; + u8 is_cs0 = 0; + u8 hash_entry_id = 0; + u8 bucket_is_overflown = 0; + u32 bucket_id = ~0; + + /* Prefetch for next iteration. */ + if (n_left_from > 1) + { + vlib_buffer_t *b1; + b1 = vlib_get_buffer (vm, from[1]); + CLIB_PREFETCH (b1, CLIB_CACHE_LINE_BYTES, STORE); + CLIB_PREFETCH (b1->data, CLIB_CACHE_LINE_BYTES, LOAD); + } + /* Dequeue a packet buffer */ + bi0 = from[0]; + from += 1; + n_left_from -= 1; + to_next[0] = bi0; + to_next += 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + ret = hicn_interest_parse_pkt (b0, &name, &namelen, &hicn0, &isv6); + + if (PREDICT_TRUE (ret == HICN_ERROR_NONE)) + { + next0 = + isv6 ? HICN_INTEREST_PCSLOOKUP_NEXT_V6_LOOKUP : + HICN_INTEREST_PCSLOOKUP_NEXT_V4_LOOKUP; + } + nameptr = (u8 *) (&name); + stats.pkts_processed++; + + if (PREDICT_FALSE (ret != HICN_ERROR_NONE || + hicn_hashtb_fullhash (nameptr, namelen, + &name_hash) != + HICN_ERROR_NONE)) + { + next0 = HICN_INTEREST_PCSLOOKUP_NEXT_ERROR_DROP; + } + else + { + if (hicn_hashtb_lookup_node (rt->pitcs->pcs_table, nameptr, + namelen, name_hash, + 0 /* is_data */ , &node_id0, + &dpo_ctx_id0, &vft_id0, &is_cs0, + &hash_entry_id, &bucket_id, + &bucket_is_overflown) == + HICN_ERROR_NONE) + { + next0 = + HICN_INTEREST_PCSLOOKUP_NEXT_INTEREST_HITPIT + is_cs0; + } + stats.pkts_interest_count++; + } + + hicn_store_internal_state (b0, name_hash, node_id0, dpo_ctx_id0, + vft_id0, hash_entry_id, bucket_id, + bucket_is_overflown); + + /* Maybe trace */ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + hicn_interest_pcslookup_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->pkt_type = HICN_PKT_TYPE_INTEREST; + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + t->next_index = next0; + } + /* + * Verify speculative enqueue, maybe switch current + * next frame + */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + u32 pit_int_count = hicn_pit_get_int_count (rt->pitcs); + u32 pit_cs_count = hicn_pit_get_cs_count (rt->pitcs); + u32 pcs_ntw_count = hicn_pcs_get_ntw_count (rt->pitcs); + + + vlib_node_increment_counter (vm, hicn_interest_pcslookup_node.index, + HICNFWD_ERROR_PROCESSED, stats.pkts_processed); + + vlib_node_increment_counter (vm, hicn_interest_pcslookup_node.index, + HICNFWD_ERROR_INTERESTS, + stats.pkts_interest_count); + + update_node_counter (vm, hicn_interest_pcslookup_node.index, + HICNFWD_ERROR_INT_COUNT, pit_int_count); + + update_node_counter (vm, hicn_interest_pcslookup_node.index, + HICNFWD_ERROR_CS_COUNT, pit_cs_count); + + update_node_counter (vm, hicn_interest_pcslookup_node.index, + HICNFWD_ERROR_CS_NTW_COUNT, pcs_ntw_count); + + return (frame->n_vectors); +} + +/* packet trace format function */ +static u8 * +hicn_interest_pcslookup_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_interest_pcslookup_trace_t *t = + va_arg (*args, hicn_interest_pcslookup_trace_t *); + + s = format (s, "INTEREST_PCSLOOKUP: pkt: %d, sw_if_index %d, next index %d", + (int) t->pkt_type, t->sw_if_index, t->next_index); + return (s); +} + + +/* + * Node registration for the interest forwarder node + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE(hicn_interest_pcslookup_node) = +{ + .function = hicn_interest_pcslookup_node_fn, + .name = "hicn-interest-pcslookup", + .vector_size = sizeof(u32), + .runtime_data_bytes = sizeof(hicn_interest_pcslookup_runtime_t), + .format_trace = hicn_interest_pcslookup_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(hicn_interest_pcslookup_error_strings), + .error_strings = hicn_interest_pcslookup_error_strings, + .n_next_nodes = HICN_INTEREST_PCSLOOKUP_N_NEXT, + .next_nodes = + { + [HICN_INTEREST_PCSLOOKUP_NEXT_V4_LOOKUP] = "ip4-lookup", + [HICN_INTEREST_PCSLOOKUP_NEXT_V6_LOOKUP] = "ip6-lookup", + [HICN_INTEREST_PCSLOOKUP_NEXT_INTEREST_HITPIT] = "hicn-interest-hitpit", + [HICN_INTEREST_PCSLOOKUP_NEXT_INTEREST_HITCS] = "hicn-interest-hitcs", + [HICN_INTEREST_PCSLOOKUP_NEXT_ERROR_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/mapme.h b/hicn-plugin/src/mapme.h new file mode 100755 index 000000000..e0786eff8 --- /dev/null +++ b/hicn-plugin/src/mapme.h @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_MAPME__ +#define __HICN_MAPME__ + +#include +#include +#include +#include + +#include "hicn.h" +#include "strategy_dpo_ctx.h" +#include "strategy_dpo_manager.h" // dpo_is_hicn + +#define HICN_MAPME_ALLOW_LOCATORS 1 + +//#define HICN_MAPME_NOTIFICATIONS 1 + +#define NOT_A_NOTIFICATION false +#define TIMER_NO_REPEAT false + +#define INVALID_SEQ 0 +#define INIT_SEQ 1 + +typedef struct hicn_mapme_conf_s +{ + hicn_mapme_conf_t conf; + bool remove_dpo; // FIXME used ? + + vlib_main_t *vm; + vlib_log_class_t log_class; +} hicn_mapme_main_t; + +#define foreach_hicn_mapme_event \ + _(FACE_ADD) \ + _(FACE_DEL) \ + _(FACE_APP_ADD) \ + _(FACE_APP_DEL) \ + _(FACE_NH_SET) \ + _(FACE_NH_ADD) \ + _(FACE_PH_ADD) \ + _(FACE_PH_DEL) + +typedef enum +{ +#define _(a) HICN_MAPME_EVENT_##a, + foreach_hicn_mapme_event +#undef _ +} hicn_mapme_event_t; + +typedef hicn_dpo_ctx_t hicn_mapme_tfib_t; + +/* + * Ideally we might need to care about alignment, but this struct is only + * used for casting hicn_dpo_ctx_t. + * + * See otherwise vnet/dpo/dpo.h + */ + +STATIC_ASSERT (sizeof (hicn_mapme_tfib_t) <= sizeof (hicn_dpo_ctx_t), + "hicn_mapme_tfib_t is greater than hicn_dpo_ctx_t"); + +#define TFIB(dpo) ((hicn_mapme_tfib_t*)(dpo)) + +static_always_inline int +hicn_mapme_nh_set (hicn_mapme_tfib_t * tfib, dpo_id_t * face_id) +{ + tfib->next_hops[0] = *face_id; + tfib->entry_count = 1; + return 0; +} + +/** + * @brief Add a next hop iif it is not already a next hops + */ +static_always_inline int +hicn_mapme_nh_add (hicn_mapme_tfib_t * tfib, dpo_id_t * face_id) +{ + for (u8 pos = 0; pos < tfib->entry_count; pos++) + if (dpo_cmp (&tfib->next_hops[pos], face_id) == 0) + return 0; + tfib->next_hops[tfib->entry_count++] = *face_id; + return 0; +} + +/** + * Add a 'previous' hop to the TFIB + * + * XXX we should have the for look in the reverse order for simpler code. + */ +static_always_inline int +hicn_mapme_tfib_add (hicn_mapme_tfib_t * tfib, dpo_id_t * face_id) +{ + u8 pos = HICN_PARAM_FIB_ENTRY_NHOPS_MAX - tfib->tfib_entry_count; + + //XXX don 't add if it already exist + // eg.an old IU received on a face on which we are retransmitting + for (u8 pos2 = pos; pos2 < HICN_PARAM_FIB_ENTRY_NHOPS_MAX; pos2++) + if (dpo_cmp (&tfib->next_hops[pos2], face_id) == 0) + return 0; + + //Make sure we have enough room + if (pos <= tfib->entry_count) + return -1; + + tfib->next_hops[pos - 1] = *face_id; + tfib->tfib_entry_count++; + + return 0; +} + +static_always_inline int +hicn_mapme_tfib_del (hicn_mapme_tfib_t * tfib, dpo_id_t * face_id) +{ + /* + * We need to do a linear scan of TFIB entries to find the one to + * remove + */ + u8 start_pos = HICN_PARAM_FIB_ENTRY_NHOPS_MAX - tfib->tfib_entry_count; + u8 pos = ~0; + for (pos = start_pos; pos < HICN_PARAM_FIB_ENTRY_NHOPS_MAX; pos++) + if (dpo_cmp (&tfib->next_hops[pos], face_id) == 0) + break; + if (pos == HICN_PARAM_FIB_ENTRY_NHOPS_MAX) + /* Not found */ + return -1; + + tfib->tfib_entry_count--; + + /* Likely we won't receive a new IU twice from the same face */ + if (PREDICT_TRUE (pos > start_pos)) + memmove (tfib->next_hops + start_pos, tfib->next_hops + start_pos + 1, + (pos - start_pos) * sizeof (dpo_id_t)); + + return 0; +} + +/** + * @brief Performs an Exact Prefix Match lookup on the FIB + * @returns the corresponding DPO (hICN or IP LB), or NULL + */ +static_always_inline + dpo_id_t * fib_epm_lookup (ip46_address_t * addr, u8 plen) +{ + fib_prefix_t fib_pfx; + fib_node_index_t fib_entry_index; + u32 fib_index; + dpo_id_t *dpo_id; + load_balance_t *lb; + + const dpo_id_t *load_balance_dpo_id; + + /* At this point the face exists in the face table */ + fib_prefix_from_ip46_addr (addr, &fib_pfx); + fib_pfx.fp_len = plen; + + /* Check if the route already exist in the fib : EPM */ + fib_index = fib_table_find (fib_pfx.fp_proto, HICN_FIB_TABLE); + + fib_entry_index = fib_table_lookup_exact_match (fib_index, &fib_pfx); + if (fib_entry_index == FIB_NODE_INDEX_INVALID) + return NULL; + + load_balance_dpo_id = fib_entry_contribute_ip_forwarding (fib_entry_index); + + /* The dpo is not a load balance dpo as expected */ + if (load_balance_dpo_id->dpoi_type != DPO_LOAD_BALANCE) + return NULL; + + /* former_dpo_id is a load_balance dpo */ + lb = load_balance_get (load_balance_dpo_id->dpoi_index); + + /* Check if there is only one bucket */ + + /* + * We now distinguish the case where we have an hICN route (the + * regular case), and the case where we have an IP route, to be able + * to apply MAP-Me mechanisms even to a locator IP address. + */ + + for (int i = 0; i < lb->lb_n_buckets; i++) + { + /* un-const */ + dpo_id = (dpo_id_t *) load_balance_get_bucket_i (lb, i); + + if (dpo_is_hicn (dpo_id)) + return dpo_id; + } + + /* un-const */ + return (dpo_id_t *) load_balance_dpo_id; +} + +/* DPO types */ + +extern dpo_type_t hicn_face_udp_type; +extern dpo_type_t hicn_face_ip_type; + +/* VLIB EDGE IDs */ + +/* in faces/ip/face_ip.c */ +extern u32 strategy_face_ip4_vlib_edge; +extern u32 strategy_face_ip6_vlib_edge; +/* in faces/udp/face_udp.c */ +extern u32 strategy_face_udp6_vlib_edge; +extern u32 strategy_face_udp6_vlib_edge; + + +/** + * @brief Returns the next hop vlib edge on which we can send an Interest packet. + * + * This is both used to preprocess a dpo that will be stored as a next hop in the FIB, and to determine on which node to send an Interest Update. + */ +always_inline u32 +hicn_mapme_get_dpo_vlib_edge (dpo_id_t * dpo) +{ + if (dpo->dpoi_type == hicn_face_ip_type) + { + switch (dpo->dpoi_proto) + { + case DPO_PROTO_IP4: + return strategy_face_ip4_vlib_edge; + case DPO_PROTO_IP6: + return strategy_face_ip6_vlib_edge; + default: + return ~0; + } + } + else if (dpo->dpoi_type == hicn_face_udp_type) + { + switch (dpo->dpoi_proto) + { + case DPO_PROTO_IP4: + return strategy_face_udp6_vlib_edge; + case DPO_PROTO_IP6: + return strategy_face_udp6_vlib_edge; + default: + return ~0; + } + } + else + { + return ~0; + } +} + +/** + * @brief Returns the next hop node on which we can send an Update packet + */ +always_inline char * +hicn_mapme_get_dpo_face_node (dpo_id_t * dpo) +{ + if (dpo->dpoi_type == hicn_face_ip_type) + { + switch (dpo->dpoi_proto) + { + case DPO_PROTO_IP4: + return "hicn-face-ip4-output"; + case DPO_PROTO_IP6: + return "hicn-face-ip6-output"; + default: + return NULL; + } + } + else if (dpo->dpoi_type == hicn_face_udp_type) + { + switch (dpo->dpoi_proto) + { + case DPO_PROTO_IP4: + return "hicn-face-udp4-output"; + case DPO_PROTO_IP6: + return "hicn-face-udp6-output"; + default: + return NULL; + } + } + else + { + return NULL; + } +} + + +#define DEBUG(...) vlib_log_debug(mapme_main.log_class, __VA_ARGS__) +#define WARN(...) vlib_log_warn(mapme_main.log_class, __VA_ARGS__) +#define ERROR(...) vlib_log_err(mapme_main.log_class, __VA_ARGS__) + +#endif /* __HICN_MAPME__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/mapme_ack.h b/hicn-plugin/src/mapme_ack.h new file mode 100755 index 000000000..98a219982 --- /dev/null +++ b/hicn-plugin/src/mapme_ack.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Copyright (c) 2017-2019 by Cisco Systems Inc. All Rights Reserved. + * + */ + +#ifndef HICN_MAPME_ACK_H +#define HICN_MAPME_ACK_H + +#include +#include + +/* Node context data */ +typedef struct hicn_mapme_ack_runtime_s +{ + int id; +} hicn_mapme_ack_runtime_t; + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; +} hicn_mapme_ack_trace_t; + +typedef enum +{ + HICN_MAPME_ACK_NEXT_ERROR_DROP, + HICN_MAPME_ACK_N_NEXT, +} hicn_mapme_ack_next_t; + +#endif /* HICN_MAPME_ACK_H */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/mapme_ack_node.c b/hicn-plugin/src/mapme_ack_node.c new file mode 100755 index 000000000..21e177bb6 --- /dev/null +++ b/hicn-plugin/src/mapme_ack_node.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "mapme.h" +#include "mapme_ack.h" +#include "mapme_eventmgr.h" +#include "mgmt.h" +#include "parser.h" +#include "data_fwd.h" +#include "infra.h" +#include "strategy_dpo_manager.h" +#include "error.h" +#include "state.h" + +extern hicn_mapme_main_t mapme_main; + +/* packet trace format function */ +static u8 *hicn_mapme_ack_format_trace (u8 * s, va_list * args); + + +/* Stats string values */ +static char *hicn_mapme_ack_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + +/* + * @brief Process incoming ack messages (Interest Update Ack) + * @param vm vlib main data structure + * @param b Control packet (IU) + * @param face_id Ingress face id + */ +bool +hicn_mapme_process_ack (vlib_main_t * vm, vlib_buffer_t * b, + dpo_id_t * in_face) +{ + seq_t fib_seq; + const dpo_id_t *dpo; + hicn_prefix_t prefix; + mapme_params_t params; + int rc; + + /* Parse incoming message */ + rc = + hicn_mapme_parse_packet (vlib_buffer_get_current (b), &prefix, ¶ms); + if (rc < 0) + goto ERR_PARSE; + + if (params.seq == INVALID_SEQ) + { + DEBUG ("Invalid sequence number found in IU"); + return true; + } + + dpo = fib_epm_lookup (&(prefix.name), prefix.len); + if (!dpo) + { + DEBUG ("Ignored ACK for non-existing FIB entry. Ignored."); + return true; + + } + + /* We are only expecting ACKs for hICN DPOs */ + ASSERT (dpo_is_hicn (dpo)); + + const hicn_dpo_vft_t *dpo_vft = hicn_dpo_get_vft (dpo->dpoi_type); + hicn_mapme_tfib_t *tfib = + TFIB (dpo_vft->hicn_dpo_get_ctx (dpo->dpoi_index)); + fib_seq = tfib->seq; + + /* + * As we always retransmit IU with the latest seq, we are not interested in + * ACKs with inferior seq + */ + if (params.seq < fib_seq) + { + DEBUG ("Ignored ACK for low seq"); + return true; + } + + hicn_mapme_tfib_del (tfib, in_face); + + /* + * Is the ingress face in TFIB ? if so, remove it, otherwise it might be a + * duplicate + */ + retx_t *retx = + vlib_process_signal_event_data (vm, + hicn_mapme_eventmgr_process_node.index, + HICN_MAPME_EVENT_FACE_PH_DEL, 1, + sizeof (retx_t)); + *retx = (retx_t) + { + .prefix = prefix,.dpo = *dpo}; + return true; + +ERR_PARSE: + return false; +} + +vlib_node_registration_t hicn_mapme_ack_node; + +static uword +hicn_mapme_ack_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + hicn_buffer_t *hb; + hicn_mapme_ack_next_t next_index; + u32 n_left_from, *from, *to_next; + n_left_from = frame->n_vectors; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) // buffers in the current frame + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t *b0; + u32 next0 = HICN_MAPME_ACK_NEXT_ERROR_DROP; + u32 sw_if_index0; + /* speculatively enqueue b0 to the current next frame */ + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + b0 = vlib_get_buffer (vm, bi0); + + vlib_cli_output (vm, "Received IUAck"); + hb = hicn_get_buffer (b0); + hicn_mapme_process_ack (vm, b0, &hb->face_dpo_id); + + /* Single loop: process 1 packet here */ + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + hicn_mapme_ack_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = sw_if_index0; + t->next_index = next0; + } + /* $$$$$ Done processing 1 packet here $$$$$ */ + + /* verify speculative enqueue, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } +// vlib_node_increment_counter (vm, hicn_mapme_ack_node.index, +// HICN_MAPME_ACK_ERROR_SWAPPED, pkts_swapped); + return (frame->n_vectors); +} + +/* packet trace format function */ +static u8 * +hicn_mapme_ack_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_mapme_ack_trace_t *t = va_arg (*args, hicn_mapme_ack_trace_t *); + + s = format (s, "MAPME_ACK: pkt: %d, sw_if_index %d, next index %d", + (int) t->pkt_type, t->sw_if_index, t->next_index); + return (s); +} + + +/* + * Node registration for the MAP-Me node processing special interests + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (hicn_mapme_ack_node) = +{ + .function = hicn_mapme_ack_node_fn, + .name = "hicn-mapme-ack", + .vector_size = sizeof (u32), + .runtime_data_bytes = sizeof (hicn_mapme_ack_runtime_t), + .format_trace = hicn_mapme_ack_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (hicn_mapme_ack_error_strings), + .error_strings = hicn_mapme_ack_error_strings, + .n_next_nodes = HICN_MAPME_ACK_N_NEXT, + .next_nodes = + { + [HICN_MAPME_ACK_NEXT_ERROR_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/mapme_ctrl.h b/hicn-plugin/src/mapme_ctrl.h new file mode 100755 index 000000000..e7c1cdf64 --- /dev/null +++ b/hicn-plugin/src/mapme_ctrl.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Copyright (c) 2017-2019 by Cisco Systems Inc. All Rights Reserved. + * + */ + +#ifndef HICN_MAPME_CTRL_H +#define HICN_MAPME_CTRL_H + +#include +#include + +/* Node context data */ +typedef struct hicn_mapme_ctrl_runtime_s +{ + int id; +} hicn_mapme_ctrl_runtime_t; + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; +} hicn_mapme_ctrl_trace_t; + +typedef enum +{ + HICN_MAPME_CTRL_NEXT_IP4_OUTPUT, + HICN_MAPME_CTRL_NEXT_IP6_OUTPUT, + HICN_MAPME_CTRL_NEXT_UDP46_OUTPUT, + HICN_MAPME_CTRL_NEXT_UDP66_OUTPUT, + HICN_MAPME_CTRL_NEXT_ERROR_DROP, + HICN_MAPME_CTRL_N_NEXT, +} hicn_mapme_ctrl_next_t; +/** + * @brief Returns the next hop node on which we can send an ACK packet + */ +always_inline hicn_mapme_ctrl_next_t +hicn_mapme_get_dpo_iface_node (dpo_id_t * dpo) +{ + if (dpo->dpoi_type == hicn_face_ip_type) + { + switch (dpo->dpoi_proto) + { + case DPO_PROTO_IP4: + return HICN_MAPME_CTRL_NEXT_IP4_OUTPUT; + case DPO_PROTO_IP6: + return HICN_MAPME_CTRL_NEXT_IP6_OUTPUT; + default: + return HICN_MAPME_CTRL_NEXT_ERROR_DROP; + } + } + else if (dpo->dpoi_type == hicn_face_udp_type) + { + switch (dpo->dpoi_proto) + { + case DPO_PROTO_IP4: + return HICN_MAPME_CTRL_NEXT_UDP46_OUTPUT; + case DPO_PROTO_IP6: + return HICN_MAPME_CTRL_NEXT_UDP66_OUTPUT; + default: + return HICN_MAPME_CTRL_NEXT_ERROR_DROP; + } + } + else + { + return HICN_MAPME_CTRL_NEXT_ERROR_DROP; + } +} + +#endif /* HICN_MAPME_CTRL_H */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/mapme_ctrl_node.c b/hicn-plugin/src/mapme_ctrl_node.c new file mode 100755 index 000000000..9fc0c9055 --- /dev/null +++ b/hicn-plugin/src/mapme_ctrl_node.c @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * This node processses MAP-Me control messages. + */ +#include +#include +#include + +#include "mapme.h" +#include "mapme_ctrl.h" +#include "mapme_eventmgr.h" +#include "mgmt.h" +#include "parser.h" +#include "infra.h" +#include "strategy_dpo_manager.h" +#include "error.h" +#include "state.h" + +extern hicn_mapme_main_t mapme_main; + +#define MS2NS(x) x * 1000000 + +/* Functions declarations */ + +/* packet trace format function */ +static u8 *hicn_mapme_ctrl_format_trace (u8 * s, va_list * args); + + +/* Stats string values */ +static char *hicn_mapme_ctrl_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + +/** + * Preprocess the ingress face so as to make it a candidate next hop, which is + * what MAP-Me will handle + */ +static_always_inline void +preprocess_in_face (hicn_type_t type, dpo_id_t * in, dpo_id_t * out) +{ + u32 vlib_edge = hicn_mapme_get_dpo_vlib_edge (in); + *out = *in; + out->dpoi_next_node = vlib_edge; +} + +/* + * @brief Process incoming control messages (Interest Update) + * @param vm vlib main data structure + * @param b Control packet (IU) + * @param face_id Ingress face id + * + * NOTE: + * - this function answers locally to the IU interest by replying with a Ack + * (Data) packet, unless in case of outdated information, in which we can + * consider the interest is dropped, and another IU (aka ICMP error) is sent so + * that retransmissions stop. + */ +static_always_inline bool +hicn_mapme_process_ctrl (vlib_main_t * vm, vlib_buffer_t * b, + dpo_id_t * in_face) +{ + seq_t fib_seq; + const dpo_id_t *dpo; + hicn_prefix_t prefix; + mapme_params_t params; + int rc; + + /* Parse incoming message */ + rc = + hicn_mapme_parse_packet (vlib_buffer_get_current (b), &prefix, ¶ms); + if (rc < 0) + goto ERR_PARSE; + + vlib_cli_output (vm, "IU - type:%d seq:%d len:%d", params.type, params.seq, + prefix.len); + + if (params.seq == INVALID_SEQ) + { + vlib_log_warn (mapme_main.log_class, + "Invalid sequence number found in IU"); + + return true; + } + + /* We forge the ACK which we be the packet forwarded by the node */ + hicn_mapme_create_ack (vlib_buffer_get_current (b), ¶ms); + + dpo = fib_epm_lookup (&prefix.name, prefix.len); + if (!dpo) + { +#ifdef HICN_MAPME_ALLOW_NONEXISTING_FIB_ENTRY + /* + * This might happen for a node hosting a producer which has moved. + * Destroying the face has led to removing all corresponding FIB + * entries. In that case, we need to correctly restore the FIB entries. + */ + DEBUG ("Re-creating FIB entry with next hop on connection") +#error "not implemented" +#else + //ERROR("Received IU for non-existing FIB entry"); + return false; +#endif /* HICN_MAPME_ALLOW_NONEXISTING_FIB_ENTRY */ + + } + +#ifdef HICN_MAPME_ALLOW_LOCATORS + if (!dpo_is_hicn ((dpo))) + { + /* We have an IP DPO */ + WARN ("Not implemented yet."); + return false; + } +#endif + + /* Process the hICN DPO */ + const hicn_dpo_vft_t *dpo_vft = hicn_dpo_get_vft (dpo->dpoi_type); + hicn_mapme_tfib_t *tfib = + TFIB (dpo_vft->hicn_dpo_get_ctx (dpo->dpoi_index)); + fib_seq = tfib->seq; + + if (params.seq > fib_seq) + { + DEBUG + ("Higher sequence number than FIB %d > %d, updating seq and next hops", + params.seq, fib_seq); + + /* This has to be done first to allow processing ack */ + tfib->seq = params.seq; + + // in_face and next_hops are face_id_t + + /* Remove ingress face from TFIB in case it was present */ + hicn_mapme_tfib_del (tfib, in_face); + + /* Move next hops to TFIB... but in_face... */ + for (u8 pos = 0; pos < tfib->entry_count; pos++) + { + if (dpo_cmp (&tfib->next_hops[pos], in_face) == 0) + continue; + hicn_mapme_tfib_add (tfib, &tfib->next_hops[pos]); + } + + /* ... and set ingress face as next_hop */ + hicn_mapme_nh_set (tfib, in_face); + + /* We transmit both the prefix and the full dpo (type will be needed to pick the right transmit node */ + retx_t *retx = + vlib_process_signal_event_data (vm, + hicn_mapme_eventmgr_process_node. + index, + HICN_MAPME_EVENT_FACE_NH_SET, 1, + sizeof (retx_t)); + *retx = (retx_t) + { + .prefix = prefix,.dpo = *dpo}; + + } + else if (params.seq == fib_seq) + { + DEBUG ("Same sequence number than FIB %d > %d, adding next hop", + params.seq, fib_seq); + + /* Remove ingress face from TFIB in case it was present */ + hicn_mapme_tfib_del (tfib, in_face); + + /* Add ingress face to next hops */ + hicn_mapme_nh_add (tfib, in_face); + + /* Multipath, multihoming, multiple producers or duplicate interest */ + retx_t *retx = + vlib_process_signal_event_data (vm, + hicn_mapme_eventmgr_process_node. + index, + HICN_MAPME_EVENT_FACE_NH_ADD, 1, + sizeof (retx_t)); + *retx = (retx_t) + { + .prefix = prefix,.dpo = *dpo}; + } + else // params.seq < fib_seq + { + /* + * face is propagating outdated information, we can just consider it as a + * prevHops + */ + hicn_mapme_tfib_add (tfib, in_face); + + retx_t *retx = + vlib_process_signal_event_data (vm, + hicn_mapme_eventmgr_process_node. + index, + HICN_MAPME_EVENT_FACE_PH_ADD, 1, + sizeof (retx_t)); + *retx = (retx_t) + { + .prefix = prefix,.dpo = *dpo}; + } + + /* We just raise events, the event_mgr is in charge of forging packet. */ + + return true; + +//ERR_ACK_CREATE: +ERR_PARSE: + return false; +} + +vlib_node_registration_t hicn_mapme_ctrl_node; + +static uword +hicn_mapme_ctrl_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + hicn_buffer_t *hb; + hicn_mapme_ctrl_next_t next_index; + u32 n_left_from, *from, *to_next; + n_left_from = frame->n_vectors; + dpo_id_t in_face; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) // buffers in the current frame + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t *b0; + + /* speculatively enqueue b0 to the current next frame */ + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + b0 = vlib_get_buffer (vm, bi0); + hb = hicn_get_buffer (b0); + + /* This determines the next node on which the ack will be sent back */ + u32 next0 = hicn_mapme_get_dpo_iface_node (&hb->face_dpo_id); + + /* Preprocessing is needed to precompute in the dpo the next node + * that will have to be followed by regular interests when being + * forwarder on a given next hop + */ + preprocess_in_face (hb->type, &hb->face_dpo_id, &in_face); + hicn_mapme_process_ctrl (vm, b0, &in_face); + + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + // vlib_node_increment_counter (vm, hicn_mapme_ctrl_node.index, + // HICN_MAPME_CTRL_ERROR_SWAPPED, pkts_swapped); + return frame->n_vectors; +} + +/* packet trace format function */ +static u8 * +hicn_mapme_ctrl_format_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_mapme_ctrl_trace_t *t = va_arg (*args, hicn_mapme_ctrl_trace_t *); + + s = format (s, "MAPME_CTRL: pkt: %d, sw_if_index %d, next index %d", + (int) t->pkt_type, t->sw_if_index, t->next_index); + return (s); +} + + +/* + * Node registration for the MAP-Me node processing special interests + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (hicn_mapme_ctrl_node) = +{ + .function = hicn_mapme_ctrl_node_fn, + .name = "hicn-mapme-ctrl", + .vector_size = sizeof (u32), + .runtime_data_bytes = sizeof (hicn_mapme_ctrl_runtime_t), + .format_trace = hicn_mapme_ctrl_format_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (hicn_mapme_ctrl_error_strings), + .error_strings = hicn_mapme_ctrl_error_strings, + .n_next_nodes = HICN_MAPME_CTRL_N_NEXT, + .next_nodes = + { + /* + * Control packets are not forwarded by this node, but sent by the Event + * Manager. This node is only responsible for sending ACK back, + * Acks are like data packets are output on iface's + */ + [HICN_MAPME_CTRL_NEXT_IP4_OUTPUT] = "hicn-iface-ip4-output", + [HICN_MAPME_CTRL_NEXT_IP6_OUTPUT] = "hicn-iface-ip6-output", + [HICN_MAPME_CTRL_NEXT_UDP46_OUTPUT] = "hicn-iface-udp4-output", + [HICN_MAPME_CTRL_NEXT_UDP66_OUTPUT] = "hicn-iface-udp6-output", + [HICN_MAPME_CTRL_NEXT_ERROR_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/mapme_eventmgr.c b/hicn-plugin/src/mapme_eventmgr.c new file mode 100755 index 000000000..5d5916403 --- /dev/null +++ b/hicn-plugin/src/mapme_eventmgr.c @@ -0,0 +1,559 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hicn.h" +#include "strategy_dpo_ctx.h" +#include "mapme.h" +#include "mapme_eventmgr.h" +#include "strategies/dpo_mw.h" + +#include +#include + +#define DEFAULT_TIMEOUT 1.0 /* s */ + +hicn_mapme_main_t mapme_main; + +hicn_prefix_t *retx_pool; +uword *retx_hash; + +void +hicn_mapme_init (vlib_main_t * vm) +{ + mapme_main.vm = vm; + mapme_main.log_class = vlib_log_register_class ("hicn_mapme", 0); +} + +/* borrowed from vnet/fib/ip4_fib.c */ + +typedef struct ip4_fib_show_walk_ctx_t_ +{ + fib_node_index_t *ifsw_indicies; +} ip4_fib_show_walk_ctx_t; + +static fib_table_walk_rc_t +ip4_fib_show_walk_cb (fib_node_index_t fib_entry_index, void *arg) +{ + ip4_fib_show_walk_ctx_t *ctx = arg; + + vec_add1 (ctx->ifsw_indicies, fib_entry_index); + + return (FIB_TABLE_WALK_CONTINUE); +} + +/* borrowed from vnet/fib/ip6_fib.c */ + +typedef struct ip6_fib_show_ctx_t_ +{ + fib_node_index_t *entries; +} ip6_fib_show_ctx_t; + +static fib_table_walk_rc_t +ip6_fib_table_show_walk (fib_node_index_t fib_entry_index, void *arg) +{ + ip6_fib_show_ctx_t *ctx = arg; + + vec_add1 (ctx->entries, fib_entry_index); + + return (FIB_TABLE_WALK_CONTINUE); +} + +void +hicn_mapme_process_fib_entry (vlib_main_t * vm, dpo_id_t face, + const fib_node_index_t * fib_entry_index) +{ + const dpo_id_t *load_balance_dpo_id; + load_balance_t *lb; + dpo_id_t *dpo_id; + fib_entry_t *fib_entry; + + load_balance_dpo_id = fib_entry_contribute_ip_forwarding (*fib_entry_index); + + /* The dpo is not a load balance dpo as expected */ + if (load_balance_dpo_id->dpoi_type != DPO_LOAD_BALANCE) + return; + + /* former_dpo_id is a load_balance dpo */ + lb = load_balance_get (load_balance_dpo_id->dpoi_index); + + for (int i = 0; i < lb->lb_n_buckets; i++) + { + /* un-const */ + dpo_id = (dpo_id_t *) load_balance_get_bucket_i (lb, i); + + if (dpo_is_hicn (dpo_id)) + { + fib_entry = fib_entry_get (*fib_entry_index); + vlib_cli_output (vm, "set face pending %U", format_fib_prefix, + &fib_entry->fe_prefix); + } + } +} + +void +hicn_mapme_process_ip4_fib (vlib_main_t * vm, dpo_id_t face) +{ + ip4_main_t *im4 = &ip4_main; + fib_table_t *fib_table; + int table_id = -1, fib_index = ~0; + + /* *INDENT-OFF* */ + pool_foreach (fib_table, im4->fibs, + ({ + ip4_fib_t *fib = pool_elt_at_index(im4->v4_fibs, fib_table->ft_index); + + if (table_id >= 0 && table_id != (int)fib->table_id) + continue; + if (fib_index != ~0 && fib_index != (int)fib->index) + continue; + + fib_node_index_t *fib_entry_index; + ip4_fib_show_walk_ctx_t ctx = { + .ifsw_indicies = NULL, + }; + + ip4_fib_table_walk(fib, ip4_fib_show_walk_cb, &ctx); + //vec_sort_with_function(ctx.ifsw_indicies, fib_entry_cmp_for_sort); + + vec_foreach(fib_entry_index, ctx.ifsw_indicies) + { + hicn_mapme_process_fib_entry(vm, face, fib_entry_index); + } + + vec_free(ctx.ifsw_indicies); + })); + /* *INDENT-ON* */ +} + +void +hicn_mapme_process_ip6_fib (vlib_main_t * vm, dpo_id_t face) +{ + /* Walk IPv6 FIB */ + ip6_main_t *im6 = &ip6_main; + fib_table_t *fib_table; + ip6_fib_t *fib; + int table_id = -1, fib_index = ~0; + + /* *INDENT-OFF* */ + pool_foreach (fib_table, im6->fibs, + ({ + fib = pool_elt_at_index(im6->v6_fibs, fib_table->ft_index); + + if (table_id >= 0 && table_id != (int)fib->table_id) + continue; + if (fib_index != ~0 && fib_index != (int)fib->index) + continue; + if (fib_table->ft_flags & FIB_TABLE_FLAG_IP6_LL) + continue; + + fib_node_index_t *fib_entry_index; + ip6_fib_show_ctx_t ctx = { + .entries = NULL, + }; + + ip6_fib_table_walk(fib->index, ip6_fib_table_show_walk, &ctx); + //vec_sort_with_function(ctx.entries, fib_entry_cmp_for_sort); + + vec_foreach(fib_entry_index, ctx.entries) + { + hicn_mapme_process_fib_entry(vm, face, fib_entry_index); + } + + vec_free(ctx.entries); + + })); + /* *INDENT-ON* */ +} + + +/** + * Callback called everytime a new face is created (not including app faces) + */ +void +hicn_mapme_on_face_added (vlib_main_t * vm, dpo_id_t face) +{ + hicn_mapme_process_ip4_fib (vm, face); + hicn_mapme_process_ip6_fib (vm, face); +} + +/* + * We need a retransmission pool holding all necessary information for crafting + * special interests, thus including both the DPO and the prefix associated to + * it. + */ +#define NUM_RETX_ENTRIES 100 +#define NUM_RETX_SLOT 2 +#define NEXT_SLOT(cur) (1-cur) +#define CUR retx_array[cur] +#define NXT retx_array[NEXT_SLOT(cur)] +#define CURLEN retx_len[cur] +#define NXTLEN retx_len[NEXT_SLOT(cur)] + +static_always_inline void * +get_packet_buffer (vlib_main_t * vm, u32 node_index, u32 dpoi_index, + ip46_address_t * addr, hicn_type_t type) +{ + vlib_frame_t *f; + vlib_buffer_t *b; // for newly created packet + u32 *to_next; + u32 bi; + u8 *buffer; + + if (vlib_buffer_alloc (vm, &bi, 1) != 1) + { + clib_warning ("buffer allocation failure"); + return NULL; + } + + /* Create a new packet from scratch */ + b = vlib_get_buffer (vm, bi); + ASSERT (b->current_data == 0); + + /* Face information for next hop node index */ + vnet_buffer (b)->ip.adj_index[VLIB_TX] = dpoi_index; + hicn_get_buffer (b)->type = type; + + /* Enqueue the packet right now */ + f = vlib_get_frame_to_node (vm, node_index); + to_next = vlib_frame_vector_args (f); + to_next[0] = bi; + f->n_vectors = 1; + vlib_put_frame_to_node (vm, node_index, f); + + // pointer to IP layer ? do we need to prepare for ethernet ??? + buffer = vlib_buffer_get_current (b); + b->current_length = + (type.l1 == IPPROTO_IPV6) ? HICN_MAPME_V6_HDRLEN : HICN_MAPME_V4_HDRLEN; + + return buffer; +} + +static_always_inline bool +hicn_mapme_send_message (vlib_main_t * vm, const hicn_prefix_t * prefix, + mapme_params_t * params, dpo_id_t * face) +{ + size_t n; + + /* This should be retrieved from face information */ + DEBUG ("Retransmission for prefix %U seq=%d", format_ip46_address, + &prefix->name, IP46_TYPE_ANY, params->seq); + + char *node_name = hicn_mapme_get_dpo_face_node (face); + if (!node_name) + { + clib_warning + ("Could not determine next node for sending MAP-Me packet"); + return false; + } + + vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) node_name); + u32 node_index = node->index; + + u8 *buffer = + get_packet_buffer (vm, node_index, face->dpoi_index, + (ip46_address_t *) prefix, + (params->protocol == + IPPROTO_IPV6) ? HICN_TYPE_IPV6_ICMP : + HICN_TYPE_IPV4_ICMP); + n = hicn_mapme_create_packet (buffer, prefix, params); + if (n <= 0) + { + clib_warning ("Could not create MAP-Me packet"); + return false; + } + + return true; +} + +static_always_inline void +hicn_mapme_send_updates (vlib_main_t * vm, hicn_prefix_t * prefix, + dpo_id_t dpo, bool send_all) +{ + const hicn_dpo_vft_t *dpo_vft = hicn_dpo_get_vft (dpo.dpoi_type); + hicn_mapme_tfib_t *tfib = TFIB (dpo_vft->hicn_dpo_get_ctx (dpo.dpoi_index)); + u8 tfib_last_idx = HICN_PARAM_FIB_ENTRY_NHOPS_MAX - tfib->tfib_entry_count; + if (!tfib) + { + DEBUG ("NULL TFIB entry id=%d", dpo.dpoi_index); + return; + } + + mapme_params_t params = { + .protocol = ip46_address_is_ip4 (&prefix->name) + ? IPPROTO_IP : IPPROTO_IPV6, + .type = UPDATE, + .seq = tfib->seq, + }; + + if (send_all) + { + for (u8 pos = tfib_last_idx; pos < HICN_PARAM_FIB_ENTRY_NHOPS_MAX; + pos++) + { + hicn_mapme_send_message (vm, prefix, ¶ms, + &tfib->next_hops[pos]); + } + } + else + { + hicn_mapme_send_message (vm, prefix, ¶ms, + &tfib->next_hops[tfib_last_idx]); + } +} + +static uword +hicn_mapme_eventmgr_process (vlib_main_t * vm, + vlib_node_runtime_t * rt, vlib_frame_t * f) +{ + f64 timeout = 0; /* By default, no timer is run */ + f64 current_time, due_time; + u8 idle = 0; + + retx_t retx_array[NUM_RETX_SLOT][NUM_RETX_ENTRIES]; + u8 retx_len[NUM_RETX_SLOT] = { 0 }; + u8 cur = 0; /* current slot */ + + hicn_mapme_init (vm); + + for (;;) + { + /* NOTE: returned timeout seems to always be 0 with get_event_data + * instead of get_event, and we thus need to reimplement timeout + * management on top, as done elsewhere in VPP code. + * + * The most probable event. For simplicity, for new faces, we pass the same retx_t with no + * prefix + */ + if (timeout != 0) + { + /* timeout = */ vlib_process_wait_for_event_or_clock (vm, timeout); + current_time = vlib_time_now (vm); + + /* + * As we don't accummulate errors, we allow for simple timer + * management with no error correction accounting for elapsed time. + * Also, we only run a timer when there are pending retransmissions. + */ + timeout = + (due_time > + current_time) ? due_time - current_time : DEFAULT_TIMEOUT; + due_time = current_time + timeout; + } + else + { + vlib_process_wait_for_event (vm); + } + + uword event_type = ~0; + void *event_data = vlib_process_get_event_data (vm, &event_type); + + switch (event_type) + { + case HICN_MAPME_EVENT_FACE_ADD: + { + /* + * A face has been added: + * - In case of a local app face, we need to advertise a new prefix + * - For another local face type, we need to advertise local + * prefixes and schedule retransmissions + */ + retx_t *retx_events = event_data; + for (u8 i = 0; i < vec_len (retx_events); i++) + { + hicn_mapme_on_face_added (vm, retx_events[i].dpo); + } + idle = 0; + } + break; + + case HICN_MAPME_EVENT_FACE_DEL: + idle = 0; + break; + + case HICN_MAPME_EVENT_FACE_NH_SET: + { + /* + * An hICN FIB entry has been modified. All operations so far + * have been procedded in the nodes. Here we need to track + * retransmissions upon timeout: we mark the FIB entry as pending in + * the second-to-next slot + */ + + /* Mark FIB entry as pending for second-to-next slot */ + retx_t *retx_events = event_data; + for (u8 i = 0; i < vec_len (retx_events); i++) + { + /* + * retx_events[i] corresponds to the dpoi_index of the (T)FIB + * structure that has been modified. Multiple successive + * events might correspond to the same entry. + * + * The FIB entry has a new next hop, and its TFIB section has: + * - eventually previous prev hops for which a IU with a + * lower seqno has been sent + * - the prev hops that have just been added. + * + * We don't distinguish any and just send an updated IU to all + * of them. The retransmission of the latest IU to all + * facilitates the matching of ACKs to a single seqno which is + * the one stored in the FIB. + * + * Since we retransmit to all prev hops, we can remove this + * (T)FIB entry for the check at the end of the current slot. + */ + retx_t *retx = (retx_t *) & retx_events[i]; + + /* + * Transmit IU for all TFIB entries with latest seqno (we have + * at least one for sure!) + */ + hicn_mapme_send_updates (vm, &retx->prefix, retx->dpo, true); + + /* Delete entry_id from retransmissions in the current slot (if present) ... */ + for (u8 j = 0; j < CURLEN; j++) + if (dpo_cmp (&(CUR[j].dpo), &retx->dpo)) + { + CUR[j].dpo.dpoi_index = ~0; /* sufficient */ + } + + /* ... and schedule it for next slot (if not already) */ + u8 j; + for (j = 0; j < NXTLEN; j++) + if (dpo_cmp (&NXT[j].dpo, &retx->dpo)) + break; + if (j == NXTLEN) /* not found */ + NXT[NXTLEN++] = *retx; + } + idle = 0; + } + break; + + case HICN_MAPME_EVENT_FACE_NH_ADD: + /* + * As per the description of states, this event should add the face + * to the list of next hops, and eventually remove it from TFIB. + * This corresponds to the multipath case. + * + * In all cases, we assume the propagation was already done when the first + * interest with the same sequence number was received, so we stop here + * No change in TFIB = no IU to send + * + * No change in timers. + */ + vlib_cli_output (vm, "[hicn_event_mgr] ADD NEXT HOP IN FIB"); + + /* Add ingress face as next hop */ + idle = 0; + + break; + + case HICN_MAPME_EVENT_FACE_PH_ADD: + /* Back-propagation, interesting even for IN (desync) */ + { + retx_t *retx_events = event_data; + for (u8 i = 0; i < vec_len (retx_events); i++) + { + hicn_mapme_send_updates (vm, &retx_events[i].prefix, + retx_events[i].dpo, false); + } + idle = 0; + } + break; + + case HICN_MAPME_EVENT_FACE_PH_DEL: + /* Ack : remove an element from TFIB */ + break; + + case ~0: + /* Timeout occurred, we have to retransmit IUs for all pending + * prefixes having entries in TFIB + * + * timeouts are slotted + * | | | | + * + * ^ + * +- event occurred + * new face, wait for the second next + * (having two arrays and swapping cur and next) + * retx : put in next + */ + idle += 1; + for (u8 pos = 0; pos < CURLEN; pos++) + { + retx_t *retx = &CUR[pos]; + + if (retx->dpo.dpoi_index == ~0) /* deleted entry */ + continue; + + const hicn_dpo_vft_t *dpo_vft = + hicn_dpo_get_vft (retx->dpo.dpoi_type); + hicn_mapme_tfib_t *tfib = + TFIB (dpo_vft->hicn_dpo_get_ctx (retx->dpo.dpoi_index)); + if (!tfib) + { + DEBUG ("NULL TFIB entry for dpoi_index=%d", + retx->dpo.dpoi_index); + continue; + } + + hicn_mapme_send_updates (vm, &retx->prefix, retx->dpo, true); + + /* + * We did some retransmissions, so let's reschedule a check in the + * next slot + */ + NXT[NXTLEN++] = CUR[pos]; + idle = 0; + } + + /* Reset events in this slot and prepare for next one */ + CURLEN = 0; + cur = NEXT_SLOT (cur); + + /* After two empty slots, we disable the timer */ + + break; + } + + if (event_data) + vlib_process_put_event_data (vm, event_data); + + timeout = (idle > 1) ? 0 : DEFAULT_TIMEOUT; + + // if (vlib_process_suspend_time_is_zero (timeout)) { ... } + + } + + /* NOTREACHED */ + return 0; +} + +/* Not static as we need to access it from hicn_face */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (hicn_mapme_eventmgr_process_node) = { //,static) = { + .function = hicn_mapme_eventmgr_process, + .type = VLIB_NODE_TYPE_PROCESS, + .name = "mapme-eventmgr-process", + .process_log2_n_stack_bytes = 16, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/mapme_eventmgr.h b/hicn-plugin/src/mapme_eventmgr.h new file mode 100755 index 000000000..2f8106d6c --- /dev/null +++ b/hicn-plugin/src/mapme_eventmgr.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include // vlib_node_registration_t (vlib/node.h) + +/* + * Structure carrying all necessary information for managing Special Interest + * (re)transmissions. + */ +typedef struct +{ + hicn_prefix_t prefix; + dpo_id_t dpo; +} retx_t; + +#define HASH32(x) ((u16)x ^ (x << 16)) + +/** + * @brief This is a process node reacting to face events. + */ +// not static ! +vlib_node_registration_t hicn_mapme_eventmgr_process_node; + +/** + * @brief Initialize MAP-Me on forwarder + * @params vm - vlib_main_t pointer + */ +void hicn_mapme_init (vlib_main_t * vm); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/mgmt.c b/hicn-plugin/src/mgmt.c new file mode 100755 index 000000000..b992ba15c --- /dev/null +++ b/hicn-plugin/src/mgmt.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "hicn.h" +#include "infra.h" +#include "mgmt.h" + +/* define message IDs */ +#include "hicn_msg_enum.h" + +/* shared routine betweeen API and CLI, leveraging API message structure */ +int +hicn_mgmt_node_stats_get (vl_api_hicn_api_node_stats_get_reply_t * rmp) +{ + rmp->pkts_processed = 0; + rmp->pkts_interest_count = 0; + rmp->pkts_data_count = 0; + rmp->pkts_from_cache_count = 0; + rmp->pkts_no_pit_count = 0; + rmp->pit_expired_count = 0; + rmp->cs_expired_count = 0; + rmp->cs_lru_count = 0; + rmp->pkts_drop_no_buf = 0; + rmp->interests_aggregated = 0; + rmp->interests_retx = 0; + rmp->pit_entries_count = + clib_host_to_net_u64 (hicn_main.pitcs.pcs_pit_count); + rmp->cs_entries_count = clib_host_to_net_u64 (hicn_main.pitcs.pcs_cs_count); + rmp->cs_entries_ntw_count = + clib_host_to_net_u64 (hicn_main.pitcs.policy_state.count); + + vlib_error_main_t *em; + vlib_node_t *n; + foreach_vlib_main (( + { + em = &this_vlib_main->error_main; + n = + vlib_get_node (this_vlib_main, + hicn_interest_pcslookup_node.index); + u32 node_cntr_base_idx = n->error_heap_index; + rmp->pkts_processed += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + HICNFWD_ERROR_PROCESSED]); + rmp->pkts_interest_count += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + HICNFWD_ERROR_INTERESTS]); + n = + vlib_get_node (this_vlib_main, + hicn_data_pcslookup_node.index); + node_cntr_base_idx = n->error_heap_index; + rmp->pkts_processed += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + HICNFWD_ERROR_PROCESSED]); + n = + vlib_get_node (this_vlib_main, + hicn_data_pcslookup_node.index); + node_cntr_base_idx = n->error_heap_index; + rmp->pkts_data_count += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + HICNFWD_ERROR_DATAS]); + n = + vlib_get_node (this_vlib_main, + hicn_interest_hitcs_node.index); + node_cntr_base_idx = n->error_heap_index; + rmp->pkts_from_cache_count += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + HICNFWD_ERROR_CACHED]); + n = + vlib_get_node (this_vlib_main, + hicn_interest_hitpit_node.index); + node_cntr_base_idx = n->error_heap_index; + rmp->interests_aggregated += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + HICNFWD_ERROR_INTEREST_AGG]); + rmp->interests_retx += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + HICNFWD_ERROR_INT_RETRANS]);})); + return (HICN_ERROR_NONE); +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/mgmt.h b/hicn-plugin/src/mgmt.h new file mode 100755 index 000000000..08b1de089 --- /dev/null +++ b/hicn-plugin/src/mgmt.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_MGMT_H__ +#define __HICN_MGMT_H__ + +#include +#include "faces/face.h" +#include "hicn_api.h" + +typedef struct icn_stats_s +{ + u32 pkts_processed; + u32 pkts_interest_count; + u32 pkts_data_count; + u32 pkts_from_cache_count; + u32 pkts_no_pit_count; + u32 pit_expired_count; + u32 cs_expired_count; + u32 no_bufs_count; + u32 pkts_interest_agg; + u32 pkts_int_retrans; + u32 pit_int_count; + u32 pit_cs_count; +} icn_stats_t; + +typedef enum +{ + HICN_MGMT_FACE_OP_NONE = 0, + HICN_MGMT_FACE_OP_CREATE, + HICN_MGMT_FACE_OP_DELETE, + HICN_MGMT_FACE_OP_ADMIN, + HICN_MGMT_FACE_OP_HELLO, +} hicn_mgmt_face_op_e; + + +typedef enum +{ + HICN_MGMT_PUNTING_OP_NONE = 0, + HICN_MGMT_PUNTING_OP_CREATE, + HICN_MGMT_PUNTING_OP_DELETE, + HICN_MGMT_PUNTING_OP_ENABLE, + HICN_MGMT_PUNTING_OP_DISABLE +} hicn_mgmt_punting_op_e; + +typedef enum +{ + HICN_MGMT_MAPME_OP_NONE = 0, + HICN_MGMT_MAPME_OP_CREATE, + HICN_MGMT_MAPME_OP_DELETE, + HICN_MGMT_MAPME_OP_ENABLE, + HICN_MGMT_MAPME_OP_DISABLE +} hicn_mgmt_mapme_op_e; + +typedef enum +{ + HICN_ADDRESS_TYPE_NONE, + HICN_ADDRESS_TYPE_V4, + HICN_ADDRESS_TYPE_V6 +} hicn_address_type_e; + +/* + * Utility to update error counters in all hICN nodes + */ +always_inline void +update_node_counter (vlib_main_t * vm, u32 node_idx, u32 counter_idx, u64 val) +{ + vlib_node_t *node = vlib_get_node (vm, node_idx); + vlib_error_main_t *em = &(vm->error_main); + u32 base_idx = node->error_heap_index; + + em->counters[base_idx + counter_idx] = val; +} + + +/* + * Stats for the forwarding node, which end up called "error" even though + * they aren't... + */ +#define foreach_hicnfwd_error \ + _(PROCESSED, "hICN packets processed") \ + _(INTERESTS, "hICN interests forwarded") \ + _(DATAS, "hICN data msgs forwarded") \ + _(CACHED, "Cached data ") \ + _(NO_PIT, "hICN no PIT entry drops") \ + _(PIT_EXPIRED, "hICN expired PIT entries") \ + _(CS_EXPIRED, "hICN expired CS entries") \ + _(CS_LRU, "hICN LRU CS entries freed") \ + _(NO_BUFS, "No packet buffers") \ + _(INTEREST_AGG, "Interests aggregated") \ + _(INTEREST_AGG_ENTRY, "Interest aggregated per entry") \ + _(INT_RETRANS, "Interest retransmissions") \ + _(INT_COUNT, "Interests in PIT") \ + _(CS_COUNT, "CS total entries") \ + _(CS_NTW_COUNT, "CS ntw entries") \ + _(CS_APP_COUNT, "CS app entries") \ + _(HASH_COLL_HASHTB_COUNT, "Collisions in Hash table") + +typedef enum +{ +#define _(sym, str) HICNFWD_ERROR_##sym, + foreach_hicnfwd_error +#undef _ + HICNFWD_N_ERROR, +} hicnfwd_error_t; + +/* + * Declarations + */ +clib_error_t *hicn_api_plugin_hookup (vlib_main_t * vm); + +int hicn_mgmt_node_stats_get (vl_api_hicn_api_node_stats_get_reply_t * rmp); + +#endif /* // __HICN_MGMT_H__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/params.h b/hicn-plugin/src/params.h new file mode 100755 index 000000000..fc890f602 --- /dev/null +++ b/hicn-plugin/src/params.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_PARAM_H__ +#define __HICN_PARAM_H__ + +/* + * Features + */ +#define HICN_FEATURE_CS 1 //1 enable 0 disable + +/* + * Face compile-time parameters + */ +#define HICN_PARAM_FACES_MAX 64 + +/* + * Max length for hICN names + */ +#define HICN_PARAM_HICN_NAME_LEN_MAX 20 //bytes + +// Max next - hops supported in a FIB entry +#define HICN_PARAM_FIB_ENTRY_NHOPS_MAX 5 + +// Default and limit on weight, whatever weight means +#define HICN_PARAM_FIB_ENTRY_NHOP_WGHT_DFLT 0x10 +#define HICN_PARAM_FIB_ENTRY_NHOP_WGHT_MAX 0xff + +/* + * PIT compile-time parameters + */ +#define HICN_PARAM_PIT_ENTRIES_MIN 1024 +#define HICN_PARAM_PIT_ENTRIES_DFLT 1024 * 128 +#define HICN_PARAM_PIT_ENTRIES_MAX 2 * 1024 * 1024 + +// aggregation limit(interest previous hops) +#define HICN_PARAM_PIT_ENTRY_PHOPS_MAX 516 + +// PIT lifetime limits on API override this(in seconds, long -float type) +#define HICN_PARAM_PIT_LIFETIME_BOUND_MIN_SEC 0.100L +#define HICN_PARAM_PIT_LIFETIME_BOUND_MAX_SEC 20.000L + +//PIT lifetime params if not set at API(in mseconds, integer type) +#define HICN_PARAM_PIT_LIFETIME_DFLT_MIN_MS 200 +#define HICN_PARAM_PIT_LIFETIME_DFLT_DFLT_MS 20000 +#define HICN_PARAM_PIT_LIFETIME_DFLT_MAX_MS 20000 + +// Face CS reservation params +#define HICN_PARAM_FACE_MAX_CS_RESERVED 10000 //packets +#define HICN_PARAM_FACE_MIN_CS_RESERVED 0 //packets +#define HICN_PARAM_FACE_DFT_CS_RESERVED 1000 //packets + +/* + * CS compile-time parameters + */ +#define HICN_PARAM_CS_ENTRIES_MIN 0 // can disable CS +#define HICN_PARAM_CS_ENTRIES_DFLT 4 * 1024 +#define HICN_PARAM_CS_ENTRIES_MAX 1024 * 1024 + +#define HICN_PARAM_CS_LRU_DEFAULT (16 * 1024) + +/* CS lifetime defines, in mseconds, integer type */ +#define HICN_PARAM_CS_LIFETIME_MIN 100 +#define HICN_PARAM_CS_LIFETIME_DFLT (5 * 60 * 1000) // 300 seconds +#define HICN_PARAM_CS_LIFETIME_MAX (24 * 3600 * 1000) //24 hours... + +/* CS reserved portion for applications */ +#define HICN_PARAM_CS_RESERVED_APP 30 //% + +/* Cloning parameters */ +/* ip4 */ +#define HICN_IP4_VERSION_HEADER_LENGTH 0x45 +#define HICN_IP4_PROTOCOL IP_PROTOCOL_TCP +#define HICN_IP4_TTL_DEFAULT 128 + +/* ip6 */ +#define IPV6_DEFAULT_VERSION 6 +#define IPV6_DEFAULT_TRAFFIC_CLASS 0 +#define IPV6_DEFAULT_FLOW_LABEL 0 +#define HCIN_IP6_VERSION_TRAFFIC_FLOW (IPV6_DEFAULT_VERSION << 28) | \ + (IPV6_DEFAULT_TRAFFIC_CLASS << 20) | \ + (IPV6_DEFAULT_FLOW_LABEL & 0xfffff) +#define HICN_IP6_PROTOCOL IP_PROTOCOL_TCP +#define HICN_IP6_HOP_LIMIT 0x40 + +#endif /* // __HICN_PARAM_H__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/parser.h b/hicn-plugin/src/parser.h new file mode 100755 index 000000000..cbc5696ba --- /dev/null +++ b/hicn-plugin/src/parser.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_PARSER_H__ +#define __HICN_PARSER_H__ + +#include + +#include "hicn.h" +#include "error.h" + + +/* + * Key type codes for header, header tlvs, body tlvs, and child tlvs + */ + +// FIXME(reuse lib struct, no more control ?) +enum hicn_pkt_type_e +{ + HICN_PKT_TYPE_INTEREST = 0, + HICN_PKT_TYPE_CONTENT = 1, +}; + +always_inline int +hicn_interest_parse_pkt (vlib_buffer_t * pkt, hicn_name_t * name, + u16 * namelen, hicn_header_t ** pkt_hdrp, u8 * isv6) +{ + if (pkt == NULL) + return HICN_ERROR_PARSER_PKT_INVAL; + hicn_header_t *pkt_hdr = vlib_buffer_get_current (pkt); + *pkt_hdrp = pkt_hdr; + u8 *ip_pkt = vlib_buffer_get_current (pkt); + u8 version = (pkt_hdr->v4.ip.version_ihl & 0xf0) >> 4; + *isv6 = ((version & 2) >> 1); + u8 ip_proto = (*isv6) * IPPROTO_IPV6; + u8 next_proto_offset = 6 + (1 - *isv6) * 3; + //in the ipv6 header the next header field is at byte 6 + // in the ipv4 header the protocol field is at byte 9 + hicn_type_t type = (hicn_type_t) { { + .l4 = IPPROTO_NONE,.l3 = + IPPROTO_NONE,.l2 = + ip_pkt[next_proto_offset],.l1 = + ip_proto} + }; + hicn_get_buffer (pkt)->type = type; + + hicn_ops_vft[type.l1]->get_interest_name (type, &pkt_hdr->protocol, name); + *namelen = (1 - (*isv6)) * HICN_V4_NAME_LEN + (*isv6) * HICN_V6_NAME_LEN; + + return HICN_ERROR_NONE; +} + +always_inline int +hicn_data_parse_pkt (vlib_buffer_t * pkt, hicn_name_t * name, + u16 * namelen, hicn_header_t ** pkt_hdrp, u8 * isv6) +{ + if (pkt == NULL) + return HICN_ERROR_PARSER_PKT_INVAL; + hicn_header_t *pkt_hdr = vlib_buffer_get_current (pkt); + *pkt_hdrp = pkt_hdr; + *pkt_hdrp = pkt_hdr; + u8 *ip_pkt = vlib_buffer_get_current (pkt); + u8 version = (pkt_hdr->v4.ip.version_ihl & 0xf0) >> 4; + *isv6 = ((version & 2) >> 1); + u8 ip_proto = (*isv6) * IPPROTO_IPV6; + /* + * in the ipv6 header the next header field is at byte 6 in the ipv4 + * header the protocol field is at byte 9 + */ + u8 next_proto_offset = 6 + (1 - *isv6) * 3; + hicn_type_t type = (hicn_type_t) { {.l4 = IPPROTO_NONE,.l3 = + IPPROTO_NONE,.l2 = + ip_pkt[next_proto_offset],.l1 = + ip_proto} + }; + hicn_get_buffer (pkt)->type = type; + hicn_ops_vft[type.l1]->get_data_name (type, &pkt_hdr->protocol, name); + *namelen = (1 - (*isv6)) * HICN_V4_NAME_LEN + (*isv6) * HICN_V6_NAME_LEN; + + return HICN_ERROR_NONE; +} + + +#endif /* // __HICN_PARSER_H__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/pcs.c b/hicn-plugin/src/pcs.c new file mode 100755 index 000000000..4226291a1 --- /dev/null +++ b/hicn-plugin/src/pcs.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "hashtb.h" +#include "pcs.h" +#include "cache_policies/cs_lru.h" + +int +hicn_pit_create (hicn_pit_cs_t * p, u32 num_elems) +{ + int ret = + hicn_hashtb_alloc (&p->pcs_table, num_elems, sizeof (hicn_pcs_entry_t)); + p->pcs_table->ht_flags |= HICN_HASHTB_FLAG_KEY_FMT_NAME; + + p->pcs_pit_count = p->pcs_cs_count = 0; + + p->policy_state.max = + HICN_PARAM_CS_LRU_DEFAULT - + (HICN_PARAM_CS_LRU_DEFAULT * HICN_PARAM_CS_RESERVED_APP / 100); + p->policy_state.count = 0; + p->policy_state.head = p->policy_state.tail = 0; + p->pcs_app_max = HICN_PARAM_CS_LRU_DEFAULT - p->policy_state.max; + + p->policy_vft.hicn_cs_insert = hicn_cs_lru.hicn_cs_insert; + p->policy_vft.hicn_cs_update = hicn_cs_lru.hicn_cs_update; + p->policy_vft.hicn_cs_dequeue = hicn_cs_lru.hicn_cs_dequeue; + p->policy_vft.hicn_cs_delete_get = hicn_cs_lru.hicn_cs_delete_get; + p->policy_vft.hicn_cs_trim = hicn_cs_lru.hicn_cs_trim; + + return (ret); +} + + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/pcs.h b/hicn-plugin/src/pcs.h new file mode 100755 index 000000000..375a7d537 --- /dev/null +++ b/hicn-plugin/src/pcs.h @@ -0,0 +1,836 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_PCS_H__ +#define __HICN_PCS_H__ + +#include "hashtb.h" +#include "face_db.h" +#include "strategy_dpo_manager.h" +#include "error.h" +#include "cache_policies/cs_policy.h" +#include "faces/face.h" +#include "faces/ip/dpo_ip.h" +#include "faces/app/face_prod.h" + +/* The PIT and CS are stored as a union */ +#define HICN_PIT_NULL_TYPE 0 +#define HICN_PIT_TYPE 1 +#define HICN_CS_TYPE 2 + +/* + * Definitions and Forward refs for the time counters we're trying out. + * Counters are maintained by the background process. + */ +#define SEC_MS 1000 +#define HICN_INFRA_FAST_TIMER_SECS 1 +#define HICN_INFRA_FAST_TIMER_MSECS (HICN_INFRA_FAST_TIMER_SECS * SEC_MS) +#define HICN_INFRA_SLOW_TIMER_SECS 60 +#define HICN_INFRA_SLOW_TIMER_MSECS (HICN_INFRA_SLOW_TIMER_SECS * SEC_MS) + +/* + * Max number of incoming (interest) faces supported, for now. Note that + * changing this may change alignment within the PIT struct, so be careful. + */ +typedef struct __attribute__ ((packed)) hicn_pcs_shared_s +{ + + /* Installation/creation time (vpp float units, for now) */ + f64 create_time; + + /* Expiration time (vpp float units, for now) */ + f64 expire_time; + + /* Shared 'flags' octet */ + u8 entry_flags; + + /* Needed to align for the pit or cs portion */ + u8 padding; +} hicn_pcs_shared_t; + +#define HICN_PCS_ENTRY_CS_FLAG 0x01 + +/* + * PIT entry, unioned with a CS entry below + */ +typedef struct __attribute__ ((packed)) hicn_pit_entry_s +{ + + /* Shared size 8 + 8 + 2 = 18B */ + + /* + * Egress next hop (containes the egress face) This id refers to the + * nh + */ + /* choosen in the next_hops array of the dpo */ + /* 18B + 1B = 19B */ + u8 pe_txnh; + + /* Array of faces */ + /* 24B + 32B (8B*4) =56B */ + hicn_face_db_t faces; + +} hicn_pit_entry_t; + +#define HICN_CS_ENTRY_OPAQUE_SIZE HICN_HASH_NODE_APP_DATA_SIZE - 40 + +/* + * CS entry, unioned with a PIT entry below + */ +typedef struct __attribute__ ((packed)) hicn_cs_entry_s +{ + /* 22B + 2B = 24B */ + u16 align; + + /* Packet buffer, if held */ + /* 18B + 4B = 22B */ + u32 cs_pkt_buf; + + /* Ingress face */ + /* 24B + 8B = 32B */ + //Fix alignment issues + union + { + dpo_id_t cs_rxface; + u64 cs_rxface_u64; + }; + + /* Linkage for LRU, in the form of hashtable node indexes */ + /* 32B + 8B = 40B */ + u32 cs_lru_prev; + u32 cs_lru_next; + + /* Reserved for implementing cache policy different than LRU */ + /* 40B + 56B = 96B */ + u8 opaque[HICN_CS_ENTRY_OPAQUE_SIZE]; + + +} __attribute__ ((packed)) hicn_cs_entry_t; + +/* + * Combined PIT/CS entry data structure, embedded in a hashtable entry after + * the common hashtable preamble struct. This MUST fit in the available + * (fixed) space in a hashtable node. + */ +typedef struct hicn_pcs_entry_s +{ + + hicn_pcs_shared_t shared; + + union + { + hicn_pit_entry_t pit; + hicn_cs_entry_t cs; + } u; +} hicn_pcs_entry_t; + + +/* + * Overall PIT/CS table, based on the common hashtable + */ +typedef struct hicn_pit_cs_s +{ + + hicn_hashtb_t *pcs_table; + + /* Counters for PIT/CS sentries */ + u32 pcs_pit_count; + u32 pcs_cs_count; + u32 pcs_cs_dealloc; + u32 pcs_pit_dealloc; + + /* Total size of PCS */ + u32 pcs_size; + + /* Memory reserved for appfaces */ + u32 pcs_app_max; + u32 pcs_app_count; + + hicn_cs_policy_t policy_state; + hicn_cs_policy_vft_t policy_vft; + +} hicn_pit_cs_t; + +/* Functions declarations */ +int hicn_pit_create (hicn_pit_cs_t * p, u32 num_elems); + +always_inline void +hicn_pit_to_cs (vlib_main_t * vm, hicn_pit_cs_t * pitcs, + hicn_pcs_entry_t * pcs_entry, hicn_hash_entry_t * hash_entry, + hicn_hash_node_t * node, const hicn_dpo_vft_t * dpo_vft, + dpo_id_t * hicn_dpo_id, dpo_id_t * inface_id, u8 is_appface); + +always_inline void +hicn_pcs_cs_update (vlib_main_t * vm, hicn_pit_cs_t * pitcs, + hicn_pcs_entry_t * entry, hicn_hash_node_t * node); + +always_inline void +hicn_pcs_cs_delete (vlib_main_t * vm, hicn_pit_cs_t * pitcs, + hicn_pcs_entry_t ** pcs_entry, hicn_hash_node_t ** node, + hicn_hash_entry_t * hash_entry, + const hicn_dpo_vft_t * dpo_vft, dpo_id_t * hicn_dpo_id); + +always_inline int +hicn_pcs_cs_insert (vlib_main_t * vm, hicn_pit_cs_t * pitcs, + hicn_pcs_entry_t * entry, hicn_hash_node_t * node, + hicn_hash_entry_t ** hash_entry, u64 hashval, + u32 * node_id, u8 * dpo_ctx_id, u8 * vft_id, u8 * is_cs, + u8 * hash_entry_id, u32 * bucket_id, + u8 * bucket_is_overflow); + +always_inline int +hicn_pcs_cs_insert_update (vlib_main_t * vm, hicn_pit_cs_t * pitcs, + hicn_pcs_entry_t * entry, hicn_hash_node_t * node, + hicn_hash_entry_t ** hash_entry, u64 hashval, + u32 * node_id, u8 * dpo_ctx_id, u8 * vft_id, + u8 * is_cs, u8 * hash_entry_id, u32 * bucket_id, + u8 * bucket_is_overflow); + +always_inline int +hicn_pcs_pit_insert (hicn_pit_cs_t * pitcs, hicn_pcs_entry_t * entry, + hicn_hash_node_t * node, hicn_hash_entry_t ** hash_entry, + u64 hashval, u32 * node_id, u8 * dpo_ctx_id, u8 * vft_id, + u8 * is_cs, u8 * hash_entry_id, u32 * bucket_id, + u8 * bucket_is_overflow); + +always_inline void +hicn_pcs_pit_delete (hicn_pit_cs_t * pitcs, hicn_pcs_entry_t ** pcs_entryp, + hicn_hash_node_t ** node, vlib_main_t * vm, + hicn_hash_entry_t * hash_entry, + const hicn_dpo_vft_t * dpo_vft, dpo_id_t * hicn_dpo_id); + +always_inline int +hicn_pcs_insert (vlib_main_t * vm, hicn_pit_cs_t * pitcs, + hicn_pcs_entry_t * entry, hicn_hash_node_t * node, + hicn_hash_entry_t ** hash_entry, u64 hashval, u32 * node_id, + u8 * dpo_ctx_id, u8 * vft_id, u8 * is_cs, u8 * hash_entry_id, + u32 * bucket_id, u8 * bucket_is_overflow); + +always_inline void +hicn_pcs_delete (hicn_pit_cs_t * pitcs, hicn_pcs_entry_t ** pcs_entryp, + hicn_hash_node_t ** node, vlib_main_t * vm, + hicn_hash_entry_t * hash_entry, + const hicn_dpo_vft_t * dpo_vft, dpo_id_t * hicn_dpo_id); + +always_inline void +hicn_pcs_remove_lock (hicn_pit_cs_t * pitcs, hicn_pcs_entry_t ** pcs_entryp, + hicn_hash_node_t ** node, vlib_main_t * vm, + hicn_hash_entry_t * hash_entry, + const hicn_dpo_vft_t * dpo_vft, dpo_id_t * hicn_dpo_id); + +always_inline void +hicn_cs_delete_trimmed (hicn_pit_cs_t * pitcs, hicn_pcs_entry_t ** pcs_entryp, + hicn_hash_entry_t * hash_entry, + hicn_hash_node_t ** node, vlib_main_t * vm); + +/* Function implementation */ +/* Accessor for pit/cs data inside hash table node */ +static inline hicn_pcs_entry_t * +hicn_pit_get_data (hicn_hash_node_t * node) +{ + return (hicn_pcs_entry_t *) (hicn_hashtb_node_data (node)); +} + +/* Init pit/cs data block (usually inside hash table node) */ +static inline void +hicn_pit_init_data (hicn_pcs_entry_t * p) +{ + memset (p, 0, sizeof (hicn_pcs_entry_t)); + hicn_face_bucket_t *face_bkt; + pool_get (hicn_face_bucket_pool, face_bkt); + + p->u.pit.faces.next_bucket = face_bkt - hicn_face_bucket_pool; +} + + + +static inline f64 +hicn_pcs_get_exp_time (f64 cur_time_sec, u64 lifetime_msec) +{ + return (cur_time_sec + ((f64) lifetime_msec) / SEC_MS); +} + +/* + * Configure CS LRU limit. Zero is accepted, means 'no limit', probably not a + * good choice. + */ +static inline void +hicn_pit_set_lru_max (hicn_pit_cs_t * p, u32 limit) +{ + p->policy_state.max = limit; +} + +/* + * Configure CS LRU limit. Zero is accepted, means 'no limit', probably not a + * good choice. + */ +static inline void +hicn_pit_set_lru_app_max (hicn_pit_cs_t * p, u32 limit) +{ + p->pcs_app_max = limit; +} + +/* + * Accessor for PIT interest counter. + */ +static inline u32 +hicn_pit_get_int_count (const hicn_pit_cs_t * pitcs) +{ + return (pitcs->pcs_pit_count); +} + +/* + * Accessor for PIT cs entries counter. + */ +static inline u32 +hicn_pit_get_cs_count (const hicn_pit_cs_t * pitcs) +{ + return (pitcs->pcs_cs_count); +} + +static inline u32 +hicn_pcs_get_ntw_count (const hicn_pit_cs_t * pitcs) +{ + return (pitcs->policy_state.count); +} + +static inline u32 +hicn_pit_get_htb_bucket_count (const hicn_pit_cs_t * pitcs) +{ + return (pitcs->pcs_table->ht_overflow_buckets_used); +} + +static inline int +hicn_cs_enabled (hicn_pit_cs_t * pit) +{ + switch (HICN_FEATURE_CS) + { + case 0: + default: + return (0); + case 1: + return (pit->policy_state.max > 0); + } +} + +/* + * Delete a PIT/CS entry from the hashtable, freeing the hash node struct. + * The caller's pointers are zeroed! If cs_trim is true, entry has already + * been removed from lru list The main purpose of this wrapper is helping + * maintain the per-PIT stats. + */ +always_inline void +hicn_pcs_delete_internal (hicn_pit_cs_t * pitcs, + hicn_pcs_entry_t ** pcs_entryp, + hicn_hash_entry_t * hash_entry, + hicn_hash_node_t ** node, vlib_main_t * vm, + const hicn_dpo_vft_t * dpo_vft, + dpo_id_t * hicn_dpo_id) +{ + hicn_pcs_entry_t *pcs = *pcs_entryp; + + ASSERT (pcs == hicn_hashtb_node_data (*node)); + + if (hash_entry->he_flags & HICN_HASH_ENTRY_FLAG_CS_ENTRY) + { + pitcs->pcs_cs_dealloc++; + + /* Free any associated packet buffer */ + vlib_buffer_free_one (vm, pcs->u.cs.cs_pkt_buf); + pcs->u.cs.cs_pkt_buf = ~0; + ASSERT ((pcs->u.cs.cs_lru_prev == 0) + && (pcs->u.cs.cs_lru_prev == pcs->u.cs.cs_lru_next)); + } + else + { + pitcs->pcs_pit_dealloc++; + dpo_vft->hicn_dpo_unlock_dpo_ctx (hicn_dpo_id); + + /* Flush faces */ + hicn_faces_flush (&(pcs->u.pit.faces)); + } + + hicn_hashtb_delete (pitcs->pcs_table, node, hash_entry->he_msb64); + memset (*pcs_entryp, 0, sizeof (hicn_pcs_entry_t)); + *pcs_entryp = NULL; +} + +/* + * Convert a PIT entry into a CS entry (assumes that the entry is already in + * the hashtable.) This is primarily here to maintain the internal counters. + */ +always_inline void +hicn_pit_to_cs (vlib_main_t * vm, hicn_pit_cs_t * pitcs, + hicn_pcs_entry_t * pcs_entry, hicn_hash_entry_t * hash_entry, + hicn_hash_node_t * node, const hicn_dpo_vft_t * dpo_vft, + dpo_id_t * hicn_dpo_id, dpo_id_t * inface_id, u8 is_appface) +{ + + /* + * Different from the insert node. In here we don't need to add a new + * hash entry. + */ + pitcs->pcs_pit_count--; + dpo_vft->hicn_dpo_unlock_dpo_ctx (hicn_dpo_id); + /* Flush faces */ + hicn_faces_flush (&(pcs_entry->u.pit.faces)); + memset (&(pcs_entry->u.cs), ~0, sizeof (hicn_cs_entry_t)); + + hash_entry->he_flags |= HICN_HASH_ENTRY_FLAG_CS_ENTRY; + node->hn_flags |= HICN_HASH_NODE_CS_FLAGS; + pcs_entry->shared.entry_flags |= HICN_PCS_ENTRY_CS_FLAG; + + pcs_entry->u.cs.cs_rxface = *inface_id; + + /* Update the CS according to the policy */ + hicn_cs_policy_t *policy_state; + hicn_cs_policy_vft_t *policy_vft; + + if (is_appface) + { + dpo_id_t *face_dpo = (dpo_id_t *) & (pcs_entry->u.cs.cs_rxface); + hicn_face_t *face = hicn_dpoi_get_from_idx (face_dpo->dpoi_index); + hicn_face_prod_t *prod_face = (hicn_face_prod_t *) face->data; + policy_state = &prod_face->policy; + policy_vft = &prod_face->policy_vft; + } + else + { + policy_state = &pitcs->policy_state; + policy_vft = &pitcs->policy_vft; + } + + policy_vft->hicn_cs_insert (pitcs, node, pcs_entry, policy_state); + pitcs->pcs_cs_count++; + + if (policy_state->count > policy_state->max) + { + hicn_hash_node_t *node; + hicn_pcs_entry_t *pcs_entry; + hicn_hash_entry_t *hash_entry; + policy_vft->hicn_cs_delete_get (pitcs, policy_state, + &node, &pcs_entry, &hash_entry); + + + /* + * We don't have to decrease the lock (therefore we cannot + * use hicn_pcs_cs_delete function) + */ + policy_vft->hicn_cs_dequeue (pitcs, node, pcs_entry, policy_state); + + hicn_cs_delete_trimmed (pitcs, &pcs_entry, hash_entry, &node, vm); + + /* Update the global CS counter */ + pitcs->pcs_cs_count--; + } +} + +/* Functions specific for PIT or CS */ + +always_inline void +hicn_pcs_cs_update (vlib_main_t * vm, hicn_pit_cs_t * pitcs, + hicn_pcs_entry_t * entry, hicn_hash_node_t * node) +{ + hicn_cs_policy_t *policy_state; + hicn_cs_policy_vft_t *policy_vft; + + dpo_id_t *face_dpo = (dpo_id_t *) & (entry->u.cs.cs_rxface); + policy_state = &pitcs->policy_state; + policy_vft = &pitcs->policy_vft; + + if (face_dpo->dpoi_type == hicn_face_ip_type) + { + hicn_face_t *face = hicn_dpoi_get_from_idx (face_dpo->dpoi_index); + if (face->shared.flags & HICN_FACE_FLAGS_APPFACE_PROD) + { + hicn_face_prod_t *prod_face = (hicn_face_prod_t *) face->data; + policy_state = &prod_face->policy; + policy_vft = &prod_face->policy_vft; + } + } + /* Update the CS LRU, moving this item to the head */ + policy_vft->hicn_cs_update (pitcs, node, entry, policy_state); +} + +always_inline void +hicn_pcs_cs_delete (vlib_main_t * vm, hicn_pit_cs_t * pitcs, + hicn_pcs_entry_t ** pcs_entryp, hicn_hash_node_t ** nodep, + hicn_hash_entry_t * hash_entry, + const hicn_dpo_vft_t * dpo_vft, dpo_id_t * hicn_dpo_id) +{ + if (!(hash_entry->he_flags & HICN_HASH_ENTRY_FLAG_DELETED)) + { + hicn_cs_policy_t *policy_state; + hicn_cs_policy_vft_t *policy_vft; + + dpo_id_t *face_dpo = (dpo_id_t *) & ((*pcs_entryp)->u.cs.cs_rxface); + policy_state = &pitcs->policy_state; + policy_vft = &pitcs->policy_vft; + + if (face_dpo->dpoi_type == hicn_face_ip_type) + { + hicn_face_t *face = hicn_dpoi_get_from_idx (face_dpo->dpoi_index); + if (face->shared.flags & HICN_FACE_FLAGS_APPFACE_PROD) + { + hicn_face_prod_t *prod_face = (hicn_face_prod_t *) face->data; + policy_state = &prod_face->policy; + policy_vft = &prod_face->policy_vft; + } + } + policy_vft->hicn_cs_dequeue (pitcs, (*nodep), (*pcs_entryp), + policy_state); + + /* Update the global CS counter */ + pitcs->pcs_cs_count--; + } + hash_entry->locks--; + if (hash_entry->locks == 0) + { + hicn_pcs_delete_internal + (pitcs, pcs_entryp, hash_entry, nodep, vm, dpo_vft, hicn_dpo_id); + } + else + { + hash_entry->he_flags |= HICN_HASH_ENTRY_FLAG_DELETED; + } +} + +always_inline int +hicn_pcs_cs_insert (vlib_main_t * vm, hicn_pit_cs_t * pitcs, + hicn_pcs_entry_t * entry, hicn_hash_node_t * node, + hicn_hash_entry_t ** hash_entry, u64 hashval, + u32 * node_id, u8 * dpo_ctx_id, u8 * vft_id, u8 * is_cs, + u8 * hash_entry_id, u32 * bucket_id, + u8 * bucket_is_overflow) +{ + ASSERT (entry == hicn_hashtb_node_data (node)); + + int ret = + hicn_hashtb_insert (pitcs->pcs_table, node, hash_entry, hashval, node_id, + dpo_ctx_id, vft_id, is_cs, hash_entry_id, bucket_id, + bucket_is_overflow); + + if (PREDICT_TRUE (ret == HICN_ERROR_NONE)) + { + hicn_cs_policy_t *policy_state; + hicn_cs_policy_vft_t *policy_vft; + + dpo_id_t *face_dpo = (dpo_id_t *) & (entry->u.cs.cs_rxface); + policy_state = &pitcs->policy_state; + policy_vft = &pitcs->policy_vft; + + if (face_dpo->dpoi_type == hicn_face_ip_type) + { + hicn_face_t *face = hicn_dpoi_get_from_idx (face_dpo->dpoi_index); + if (face->shared.flags & HICN_FACE_FLAGS_APPFACE_PROD) + { + hicn_face_prod_t *prod_face = (hicn_face_prod_t *) face->data; + policy_state = &prod_face->policy; + policy_vft = &prod_face->policy_vft; + } + } + policy_vft->hicn_cs_insert (pitcs, node, entry, policy_state); + pitcs->pcs_cs_count++; + + if (policy_state->count > policy_state->max) + { + hicn_hash_node_t *node; + hicn_pcs_entry_t *pcs_entry; + hicn_hash_entry_t *hash_entry; + policy_vft->hicn_cs_delete_get (pitcs, policy_state, + &node, &pcs_entry, &hash_entry); + + hicn_pcs_cs_delete (vm, pitcs, &pcs_entry, &node, hash_entry, NULL, + NULL); + } + } + return ret; +} + +/* + * Insert CS entry into the hashtable The main purpose of this wrapper is + * helping maintain the per-PIT stats. + */ +always_inline int +hicn_pcs_cs_insert_update (vlib_main_t * vm, hicn_pit_cs_t * pitcs, + hicn_pcs_entry_t * entry, hicn_hash_node_t * node, + hicn_hash_entry_t ** hash_entry, u64 hashval, + u32 * node_id, u8 * dpo_ctx_id, u8 * vft_id, + u8 * is_cs, u8 * hash_entry_id, u32 * bucket_id, + u8 * bucket_is_overflow) +{ + int ret; + + ASSERT (entry == hicn_hashtb_node_data (node)); + + ret = + hicn_pcs_cs_insert (vm, pitcs, entry, node, hash_entry, hashval, node_id, + dpo_ctx_id, vft_id, is_cs, hash_entry_id, bucket_id, + bucket_is_overflow); + + /* A content already exists in CS with the same name */ + if (ret == HICN_ERROR_HASHTB_EXIST) + { + /* Update the entry */ + hicn_hash_node_t *existing_node = + hicn_hashtb_node_from_idx (pitcs->pcs_table, *node_id); + hicn_pcs_entry_t *pitp = hicn_pit_get_data (existing_node); + + /* Free associated packet buffer and update counter */ + pitcs->pcs_cs_dealloc++; + vlib_buffer_free_one (vm, pitp->u.cs.cs_pkt_buf); + + pitp->shared.create_time = entry->shared.create_time; + pitp->shared.expire_time = entry->shared.expire_time; + pitp->u.cs.cs_pkt_buf = entry->u.cs.cs_pkt_buf; + hicn_pcs_cs_update (vm, pitcs, pitp, existing_node); + } + return (ret); +} + +/* + * Insert PIT entry into the hashtable The main purpose of this wrapper is + * helping maintain the per-PIT stats. + */ +always_inline int +hicn_pcs_pit_insert (hicn_pit_cs_t * pitcs, hicn_pcs_entry_t * entry, + hicn_hash_node_t * node, hicn_hash_entry_t ** hash_entry, + u64 hashval, u32 * node_id, u8 * dpo_ctx_id, u8 * vft_id, + u8 * is_cs, u8 * hash_entry_id, u32 * bucket_id, + u8 * bucket_is_overflow) +{ + ASSERT (entry == hicn_hashtb_node_data (node)); + + int ret = + hicn_hashtb_insert (pitcs->pcs_table, node, hash_entry, hashval, node_id, + dpo_ctx_id, vft_id, is_cs, hash_entry_id, bucket_id, + bucket_is_overflow); + + if (PREDICT_TRUE (ret == HICN_ERROR_NONE)) + pitcs->pcs_pit_count++; + + return ret; +} + +always_inline void +hicn_pcs_pit_delete (hicn_pit_cs_t * pitcs, hicn_pcs_entry_t ** pcs_entryp, + hicn_hash_node_t ** node, vlib_main_t * vm, + hicn_hash_entry_t * hash_entry, + const hicn_dpo_vft_t * dpo_vft, dpo_id_t * hicn_dpo_id) +{ + hash_entry->locks--; + pitcs->pcs_pit_count--; + if (hash_entry->locks == 0) + { + hicn_pcs_delete_internal + (pitcs, pcs_entryp, hash_entry, node, vm, dpo_vft, hicn_dpo_id); + } + else + { + hash_entry->he_flags |= HICN_HASH_ENTRY_FLAG_DELETED; + } +} + + +/* Generic functions for PIT/CS */ + +/* + * Insert PIT/CS entry into the hashtable The main purpose of this wrapper is + * helping maintain the per-PIT stats. + */ +always_inline int +hicn_pcs_insert (vlib_main_t * vm, hicn_pit_cs_t * pitcs, + hicn_pcs_entry_t * entry, hicn_hash_node_t * node, + hicn_hash_entry_t ** hash_entry, u64 hashval, u32 * node_id, + u8 * dpo_ctx_id, u8 * vft_id, u8 * is_cs, u8 * hash_entry_id, + u32 * bucket_id, u8 * bucket_is_overflow) +{ + int ret; + + if ((*hash_entry)->he_flags & HICN_HASH_ENTRY_FLAG_CS_ENTRY) + { + ret = + hicn_pcs_cs_insert (vm, pitcs, entry, node, hash_entry, hashval, + node_id, dpo_ctx_id, vft_id, is_cs, hash_entry_id, + bucket_id, bucket_is_overflow); + } + else + { + ret = + hicn_pcs_pit_insert (pitcs, entry, node, hash_entry, hashval, node_id, + dpo_ctx_id, vft_id, is_cs, hash_entry_id, + bucket_id, bucket_is_overflow); + } + + return (ret); +} + + +/* + * Delete entry if there are no pending lock on the entry, otherwise mark it + * as to delete. + */ +always_inline void +hicn_pcs_delete (hicn_pit_cs_t * pitcs, hicn_pcs_entry_t ** pcs_entryp, + hicn_hash_node_t ** nodep, vlib_main_t * vm, + hicn_hash_entry_t * hash_entry, + const hicn_dpo_vft_t * dpo_vft, dpo_id_t * hicn_dpo_id) +{ + /* + * If the entry has already been marked as deleted, it has already + * been dequeue + */ + if (hash_entry->he_flags & HICN_HASH_ENTRY_FLAG_CS_ENTRY) + { + hicn_pcs_cs_delete (vm, pitcs, pcs_entryp, nodep, hash_entry, + dpo_vft, hicn_dpo_id); + } + else + { + hicn_pcs_pit_delete (pitcs, pcs_entryp, nodep, vm, + hash_entry, dpo_vft, hicn_dpo_id); + } +} + +/* + * Remove a lock in the entry and delete it if there are no pending lock and + * the entry is marked as to be deleted + */ +always_inline void +hicn_pcs_remove_lock (hicn_pit_cs_t * pitcs, hicn_pcs_entry_t ** pcs_entryp, + hicn_hash_node_t ** node, vlib_main_t * vm, + hicn_hash_entry_t * hash_entry, + const hicn_dpo_vft_t * dpo_vft, dpo_id_t * hicn_dpo_id) +{ + hash_entry->locks--; + if (hash_entry->locks == 0 + && (hash_entry->he_flags & HICN_HASH_ENTRY_FLAG_DELETED)) + { + hicn_pcs_delete_internal + (pitcs, pcs_entryp, hash_entry, node, vm, dpo_vft, hicn_dpo_id); + } +} + +/* + * Delete entry which has already been bulk-removed from lru list + */ +always_inline void +hicn_cs_delete_trimmed (hicn_pit_cs_t * pitcs, hicn_pcs_entry_t ** pcs_entryp, + hicn_hash_entry_t * hash_entry, + hicn_hash_node_t ** node, vlib_main_t * vm) +{ + + + if (hash_entry->locks == 0) + { + const hicn_dpo_vft_t *dpo_vft = hicn_dpo_get_vft (hash_entry->vft_id); + dpo_id_t hicn_dpo_id = + { dpo_vft->hicn_dpo_get_type (), 0, 0, hash_entry->dpo_ctx_id }; + + hicn_pcs_delete_internal + (pitcs, pcs_entryp, hash_entry, node, vm, dpo_vft, &hicn_dpo_id); + } + else + { + hash_entry->he_flags |= HICN_HASH_ENTRY_FLAG_DELETED; + } +} + +/* + * wrappable counter math (assumed uint16_t): return sum of addends + */ +always_inline u16 +hicn_infra_seq16_sum (u16 addend1, u16 addend2) +{ + return (addend1 + addend2); +} + +/* + * for comparing wrapping numbers, return lt,eq,gt 0 for a lt,eq,gt b + */ +always_inline int +hicn_infra_seq16_cmp (u16 a, u16 b) +{ + return ((int16_t) (a - b)); +} + +/* + * below are wrappers for lt, le, gt, ge seq16 comparators + */ +always_inline int +hicn_infra_seq16_lt (u16 a, u16 b) +{ + return (hicn_infra_seq16_cmp (a, b) < 0); +} + +always_inline int +hicn_infra_seq16_le (u16 a, u16 b) +{ + return (hicn_infra_seq16_cmp (a, b) <= 0); +} + +always_inline int +hicn_infra_seq16_gt (u16 a, u16 b) +{ + return (hicn_infra_seq16_cmp (a, b) > 0); +} + +always_inline int +hicn_infra_seq16_ge (u16 a, u16 b) +{ + return (hicn_infra_seq16_cmp (a, b) >= 0); +} + + +extern u16 hicn_infra_fast_timer; /* Counts at 1 second intervals */ +extern u16 hicn_infra_slow_timer; /* Counts at 1 minute intervals */ + +/* + * Utilities to convert lifetime into expiry time based on compressed clock, + * suitable for the opportunistic hashtable entry timeout processing. + */ + +//convert time in msec to time in clicks +always_inline u16 +hicn_infra_ms2clicks (u64 time_ms, u64 ms_per_click) +{ + f64 time_clicks = + ((f64) (time_ms + ms_per_click - 1)) / ((f64) ms_per_click); + return ((u16) time_clicks); +} + +always_inline u16 +hicn_infra_get_fast_exp_time (u64 lifetime_ms) +{ + u16 lifetime_clicks = + hicn_infra_ms2clicks (lifetime_ms, HICN_INFRA_FAST_TIMER_MSECS); + return (hicn_infra_seq16_sum (hicn_infra_fast_timer, lifetime_clicks)); +} + +always_inline u16 +hicn_infra_get_slow_exp_time (u64 lifetime_ms) +{ + u16 lifetime_clicks = + hicn_infra_ms2clicks (lifetime_ms, HICN_INFRA_SLOW_TIMER_MSECS); + return (hicn_infra_seq16_sum (hicn_infra_slow_timer, lifetime_clicks)); +} + +#endif /* // __HICN_PCS_H__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/pg.c b/hicn-plugin/src/pg.c new file mode 100755 index 000000000..643aff2be --- /dev/null +++ b/hicn-plugin/src/pg.c @@ -0,0 +1,1147 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include "hicn.h" +#include "pg.h" +#include "parser.h" +#include "infra.h" + +/* Registration struct for a graph node */ +vlib_node_registration_t hicn_pg_interest_node; +vlib_node_registration_t hicn_pg_data_node; + +/* Stats, which end up called "error" even though they aren't... */ +#define foreach_hicnpg_error \ + _(PROCESSED, "hICN PG packets processed") \ + _(DROPPED, "hICN PG packets dropped") \ + _(INTEREST_MSGS_GENERATED, "hICN PG Interests generated") \ + _(CONTENT_MSGS_RECEIVED, "hICN PG Content msgs received") + +typedef enum +{ +#define _(sym,str) HICNPG_ERROR_##sym, + foreach_hicnpg_error +#undef _ + HICNPG_N_ERROR, +} hicnpg_error_t; + +static char *hicnpg_error_strings[] = { +#define _(sym,string) string, + foreach_hicnpg_error +#undef _ +}; + +/* + * Next graph nodes, which reference the list in the actual registration + * block below + */ +typedef enum +{ + HICNPG_INTEREST_NEXT_V4_LOOKUP, + HICNPG_INTEREST_NEXT_V6_LOOKUP, + HICNPG_INTEREST_NEXT_IFACE_IP4_INPUT, + HICNPG_INTEREST_NEXT_IFACE_IP6_INPUT, + HICNPG_INTEREST_NEXT_DROP, + HICNPG_N_NEXT, +} hicnpg_interest_next_t; + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; + u16 msg_type; +} hicnpg_trace_t; + +hicnpg_main_t hicnpg_main = { + .index = (u32) 0, + .index_ifaces = (u32) 1, + .max_seq_number = (u32) ~ 0, + .interest_lifetime = 4, + .n_flows = (u32) 0, + .n_ifaces = (u32) 1, + .hicn_underneath = 0 +}; + +hicnpg_server_main_t hicnpg_server_main = { + .node_index = 0, + .hicn_underneath = 0 +}; + +/* packet trace format function */ +static u8 * +format_hicnpg_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicnpg_trace_t *t = va_arg (*args, hicnpg_trace_t *); + + s = format (s, "HICNPG: pkt: %d, msg %d, sw_if_index %d, next index %d", + (int) t->pkt_type, (int) t->msg_type, + t->sw_if_index, t->next_index); + return (s); +} + +always_inline void +hicn_rewrite_interestv4 (vlib_main_t * vm, vlib_buffer_t * b0, u32 seq_number, + u16 lifetime, u32 next_flow, u32 iface); + +always_inline void +hicn_rewrite_interestv6 (vlib_main_t * vm, vlib_buffer_t * b0, u32 seq_number, + u16 lifetime, u32 next_flow, u32 iface); + +always_inline void +convert_interest_to_data_v4 (vlib_main_t * vm, vlib_buffer_t * b0, + vlib_buffer_t * rb, u32 bi0); + +always_inline void +convert_interest_to_data_v6 (vlib_main_t * vm, vlib_buffer_t * b0, + vlib_buffer_t * rb, u32 bi0); + +always_inline void +calculate_tcp_checksum_v4 (vlib_main_t * vm, vlib_buffer_t * b0); + +always_inline void +calculate_tcp_checksum_v6 (vlib_main_t * vm, vlib_buffer_t * b0); +/* + * Node function for the icn packet-generator client. The goal here is to + * manipulate/tweak a stream of packets that have been injected by the vpp + * packet generator to generate icn request traffic. + */ +static uword +hicnpg_client_interest_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next; + hicnpg_interest_next_t next_index; + u32 pkts_processed = 0, pkts_dropped = 0; + u32 interest_msgs_generated = 0; + u32 bi0, bi1; + vlib_buffer_t *b0, *b1; + u8 pkt_type0 = 0, pkt_type1 = 0; + u16 msg_type0 = 0, msg_type1 = 0; + hicn_header_t *hicn0 = NULL, *hicn1 = NULL; + hicn_name_t name0, name1; + u16 namelen0, namelen1; + hicnpg_main_t *hpgm = &hicnpg_main; + int iface = 0; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from >= 4 && n_left_to_next >= 2) + { + u32 next0 = HICNPG_INTEREST_NEXT_DROP; + u32 next1 = HICNPG_INTEREST_NEXT_DROP; + u32 sw_if_index0 = ~0, sw_if_index1 = ~0; + u8 isv6_0; + u8 isv6_1; + + /* Prefetch next iteration. */ + { + vlib_buffer_t *p2, *p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + CLIB_PREFETCH (p2->data, (2 * CLIB_CACHE_LINE_BYTES), STORE); + CLIB_PREFETCH (p3->data, (2 * CLIB_CACHE_LINE_BYTES), STORE); + } + + /* + * speculatively enqueue b0 and b1 to the current + * next frame + */ + to_next[0] = bi0 = from[0]; + to_next[1] = bi1 = from[1]; + from += 2; + to_next += 2; + n_left_from -= 2; + n_left_to_next -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX]; + + /* Check icn packets, locate names */ + if (hicn_interest_parse_pkt (b0, &name0, &namelen0, &hicn0, &isv6_0) + == HICN_ERROR_NONE) + { + /* this node grabs only interests */ + + /* Increment the appropriate message counter */ + interest_msgs_generated++; + + iface = (hpgm->index_ifaces % hpgm->n_ifaces); + /* Rewrite and send */ + isv6_0 ? hicn_rewrite_interestv6 (vm, b0, + (hpgm->index / + hpgm->n_flows) % + hpgm->max_seq_number, + hpgm->interest_lifetime, + hpgm->index % hpgm->n_flows, + iface) : + hicn_rewrite_interestv4 (vm, b0, + (hpgm->index / hpgm->n_flows) % + hpgm->max_seq_number, + hpgm->interest_lifetime, + hpgm->index % hpgm->n_flows, iface); + + hpgm->index_ifaces++; + if (iface == (hpgm->n_ifaces - 1)) + hpgm->index++; + + next0 = + isv6_0 ? HICNPG_INTEREST_NEXT_V6_LOOKUP : + HICNPG_INTEREST_NEXT_V4_LOOKUP; + next0 += 2 * hpgm->hicn_underneath; + } + if (hicn_interest_parse_pkt (b1, &name1, &namelen1, &hicn1, &isv6_1) + == HICN_ERROR_NONE) + { + /* this node grabs only interests */ + + /* Increment the appropriate message counter */ + interest_msgs_generated++; + + iface = (hpgm->index_ifaces % hpgm->n_ifaces); + /* Rewrite and send */ + isv6_1 ? hicn_rewrite_interestv6 (vm, b1, + (hpgm->index / + hpgm->n_flows) % + hpgm->max_seq_number, + hpgm->interest_lifetime, + hpgm->index % hpgm->n_flows, + iface) : + hicn_rewrite_interestv4 (vm, b1, + (hpgm->index / hpgm->n_flows) % + hpgm->max_seq_number, + hpgm->interest_lifetime, + hpgm->index % hpgm->n_flows, iface); + + hpgm->index_ifaces++; + if (iface == (hpgm->n_ifaces - 1)) + hpgm->index++; + + next1 = + isv6_1 ? HICNPG_INTEREST_NEXT_V6_LOOKUP : + HICNPG_INTEREST_NEXT_V4_LOOKUP; + next1 += 2 * hpgm->hicn_underneath; + } + /* Send pkt to next node */ + vnet_buffer (b0)->sw_if_index[VLIB_TX] = ~0; + vnet_buffer (b1)->sw_if_index[VLIB_TX] = ~0; + + pkts_processed += 2; + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE))) + { + if (b0->flags & VLIB_BUFFER_IS_TRACED) + { + hicnpg_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->pkt_type = pkt_type0; + t->msg_type = msg_type0; + t->sw_if_index = sw_if_index0; + t->next_index = next0; + } + if (b1->flags & VLIB_BUFFER_IS_TRACED) + { + hicnpg_trace_t *t = + vlib_add_trace (vm, node, b1, sizeof (*t)); + t->pkt_type = pkt_type1; + t->msg_type = msg_type1; + t->sw_if_index = sw_if_index1; + t->next_index = next1; + } + } + if (next0 == HICNPG_INTEREST_NEXT_DROP) + { + pkts_dropped++; + } + if (next1 == HICNPG_INTEREST_NEXT_DROP) + { + pkts_dropped++; + } + /* + * verify speculative enqueues, maybe switch current + * next frame + */ + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 next0 = HICNPG_INTEREST_NEXT_DROP; + u32 sw_if_index0; + u8 isv6_0; + + /* speculatively enqueue b0 to the current next frame */ + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + + /* Check icn packets, locate names */ + if (hicn_interest_parse_pkt (b0, &name0, &namelen0, &hicn0, &isv6_0) + == HICN_ERROR_NONE) + { + /* this node grabs only interests */ + + /* Increment the appropriate message counter */ + interest_msgs_generated++; + + iface = (hpgm->index_ifaces % hpgm->n_ifaces); + + /* Rewrite and send */ + isv6_0 ? hicn_rewrite_interestv6 (vm, b0, + (hpgm->index / + hpgm->n_flows) % + hpgm->max_seq_number, + hpgm->interest_lifetime, + hpgm->index % hpgm->n_flows, + iface) : + hicn_rewrite_interestv4 (vm, b0, + (hpgm->index / hpgm->n_flows) % + hpgm->max_seq_number, + hpgm->interest_lifetime, + hpgm->index % hpgm->n_flows, iface); + + hpgm->index_ifaces++; + if (iface == (hpgm->n_ifaces - 1)) + hpgm->index++; + + next0 = + isv6_0 ? HICNPG_INTEREST_NEXT_V6_LOOKUP : + HICNPG_INTEREST_NEXT_V4_LOOKUP; + next0 += 2 * hpgm->hicn_underneath; + } + /* Send pkt to ip lookup */ + vnet_buffer (b0)->sw_if_index[VLIB_TX] = ~0; + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + hicnpg_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->pkt_type = pkt_type0; + t->msg_type = msg_type0; + t->sw_if_index = sw_if_index0; + t->next_index = next0; + } + pkts_processed += 1; + + if (next0 == HICNPG_INTEREST_NEXT_DROP) + { + pkts_dropped++; + } + /* + * verify speculative enqueue, maybe switch current + * next frame + */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, hicn_pg_interest_node.index, + HICNPG_ERROR_PROCESSED, pkts_processed); + vlib_node_increment_counter (vm, hicn_pg_interest_node.index, + HICNPG_ERROR_DROPPED, pkts_dropped); + vlib_node_increment_counter (vm, hicn_pg_interest_node.index, + HICNPG_ERROR_INTEREST_MSGS_GENERATED, + interest_msgs_generated); + + return (frame->n_vectors); +} + +void +hicn_rewrite_interestv4 (vlib_main_t * vm, vlib_buffer_t * b0, u32 seq_number, + u16 interest_lifetime, u32 next_flow, u32 iface) +{ + hicn_header_t *h0 = vlib_buffer_get_current (b0); + + /* Generate the right src and dst corresponding to flow and iface */ + ip46_address_t src_addr = { + .ip4 = hicnpg_main.pgen_clt_src_addr.ip4, + }; + hicn_name_t dst_name = { + .ip4.prefix_as_ip4 = hicnpg_main.pgen_clt_hicn_name.ip4, + .ip4.suffix = seq_number, + }; + + src_addr.ip4.as_u32 += clib_host_to_net_u32 (iface); + dst_name.ip4.prefix_as_ip4.as_u32 += clib_net_to_host_u32 (next_flow); + + /* Update locator and name */ + hicn_type_t type = hicn_get_buffer (b0)->type; + HICN_OPS4->set_interest_locator (type, &h0->protocol, &src_addr); + HICN_OPS4->set_interest_name (type, &h0->protocol, &dst_name); + + /* Update lifetime (currently L4 checksum is not updated) */ + HICN_OPS4->set_lifetime (type, &h0->protocol, interest_lifetime); + + /* Update checksums */ + HICN_OPS4->update_checksums (type, &h0->protocol, 0, 0); +} + +/** + * @brief Rewrite the IPv6 header as the next generated packet + * + * Set up a name prefix + * - etiher generate interest in which the name varies only after the prefix + * (inc : seq_number), then the flow acts on the prefix (CHECK) + * seq_number => TCP, FLOW => + * + * SRC : pgen_clt_src_addr.ip6 DST = generate name (pgen_clt_hicn_name.ip6) + * ffff:ffff:ffff:ffff ffff:ffff:ffff:ffff + * \__/ \__/ + * +iface + flow + * Source is used to emulate different consumers. + * FIXME iface is ill-named, better name it consumer id + * Destination is used to iterate on the content. + */ +void +hicn_rewrite_interestv6 (vlib_main_t * vm, vlib_buffer_t * b0, u32 seq_number, + u16 interest_lifetime, u32 next_flow, u32 iface) +{ + hicn_header_t *h0 = vlib_buffer_get_current (b0); + + /* Generate the right src and dst corresponding to flow and iface */ + ip46_address_t src_addr = { + .ip6 = hicnpg_main.pgen_clt_src_addr.ip6, + }; + hicn_name_t dst_name = { + .ip6.prefix_as_ip6 = hicnpg_main.pgen_clt_hicn_name.ip6, + .ip6.suffix = seq_number, + }; + src_addr.ip6.as_u32[3] += clib_host_to_net_u32 (iface); + dst_name.ip6.prefix_as_ip6.as_u32[3] += clib_net_to_host_u32 (next_flow); + + /* Update locator and name */ + hicn_type_t type = hicn_get_buffer (b0)->type; + HICN_OPS6->set_interest_locator (type, &h0->protocol, &src_addr); + HICN_OPS6->set_interest_name (type, &h0->protocol, &dst_name); + + /* Update lifetime */ + HICN_OPS6->set_lifetime (type, &h0->protocol, interest_lifetime); + + /* Update checksums */ + calculate_tcp_checksum_v6 (vm, b0); +} + + + +void +calculate_tcp_checksum_v4 (vlib_main_t * vm, vlib_buffer_t * b0) +{ + ip4_header_t *ip0; + tcp_header_t *tcp0; + ip_csum_t sum0; + u32 tcp_len0; + + ip0 = (ip4_header_t *) (vlib_buffer_get_current (b0)); + tcp0 = + (tcp_header_t *) (vlib_buffer_get_current (b0) + sizeof (ip4_header_t)); + tcp_len0 = clib_net_to_host_u16 (ip0->length) - sizeof (ip4_header_t); + + /* Initialize checksum with header. */ + if (BITS (sum0) == 32) + { + sum0 = clib_mem_unaligned (&ip0->src_address, u32); + sum0 = + ip_csum_with_carry (sum0, + clib_mem_unaligned (&ip0->dst_address, u32)); + } + else + sum0 = clib_mem_unaligned (&ip0->src_address, u64); + + sum0 = ip_csum_with_carry + (sum0, clib_host_to_net_u32 (tcp_len0 + (ip0->protocol << 16))); + + /* Invalidate possibly old checksum. */ + tcp0->checksum = 0; + + u32 tcp_offset = sizeof (ip4_header_t); + sum0 = ip_incremental_checksum_buffer (vm, b0, tcp_offset, tcp_len0, sum0); + + tcp0->checksum = ~ip_csum_fold (sum0); +} + +void +calculate_tcp_checksum_v6 (vlib_main_t * vm, vlib_buffer_t * b0) +{ + ip6_header_t *ip0; + tcp_header_t *tcp0; + ip_csum_t sum0; + u32 tcp_len0; + + ip0 = (ip6_header_t *) (vlib_buffer_get_current (b0)); + tcp0 = + (tcp_header_t *) (vlib_buffer_get_current (b0) + sizeof (ip6_header_t)); + tcp_len0 = clib_net_to_host_u16 (ip0->payload_length); + + /* Initialize checksum with header. */ + if (BITS (sum0) == 32) + { + sum0 = clib_mem_unaligned (&ip0->src_address, u32); + sum0 = + ip_csum_with_carry (sum0, + clib_mem_unaligned (&ip0->dst_address, u32)); + } + else + sum0 = clib_mem_unaligned (&ip0->src_address, u64); + + sum0 = ip_csum_with_carry + (sum0, clib_host_to_net_u32 (tcp_len0 + (ip0->protocol << 16))); + + /* Invalidate possibly old checksum. */ + tcp0->checksum = 0; + + u32 tcp_offset = sizeof (ip6_header_t); + sum0 = ip_incremental_checksum_buffer (vm, b0, tcp_offset, tcp_len0, sum0); + + tcp0->checksum = ~ip_csum_fold (sum0); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE(hicn_pg_interest_node) ={ + .function = hicnpg_client_interest_node_fn, + .name = "hicnpg-interest", + .vector_size = sizeof(u32), + .format_trace = format_hicnpg_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(hicnpg_error_strings), + .error_strings = hicnpg_error_strings, + .n_next_nodes = HICNPG_N_NEXT, + .next_nodes = + { + [HICNPG_INTEREST_NEXT_V4_LOOKUP] = "ip4-lookup", + [HICNPG_INTEREST_NEXT_V6_LOOKUP] = "ip6-lookup", + [HICNPG_INTEREST_NEXT_IFACE_IP4_INPUT] = "hicn-iface-ip4-input", + [HICNPG_INTEREST_NEXT_IFACE_IP6_INPUT] = "hicn-iface-ip6-input", + [HICNPG_INTEREST_NEXT_DROP] = "error-drop" + }, +}; +/* *INDENT-ON* */ + +/* + * Next graph nodes, which reference the list in the actual registration + * block below + */ +typedef enum +{ + HICNPG_DATA_NEXT_DROP, + HICNPG_DATA_N_NEXT, +} hicnpg_data_next_t; + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; + u16 msg_type; +} icnpg_data_trace_t; + +/* packet trace format function */ +static u8 * +format_hicnpg_data_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicnpg_trace_t *t = va_arg (*args, hicnpg_trace_t *); + + s = format (s, "HICNPG: pkt: %d, msg %d, sw_if_index %d, next index %d", + (int) t->pkt_type, (int) t->msg_type, + t->sw_if_index, t->next_index); + return (s); +} + + +/* + * Node function for the icn packet-generator client. The goal here is to + * manipulate/tweak a stream of packets that have been injected by the vpp + * packet generator to generate icn request traffic. + */ +static uword +hicnpg_client_data_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next; + hicnpg_data_next_t next_index; + u32 pkts_processed = 0; + u32 content_msgs_received = 0; + u32 bi0, bi1; + vlib_buffer_t *b0, *b1; + u8 pkt_type0 = 0, pkt_type1 = 0; + u16 msg_type0 = 1, msg_type1 = 1; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from >= 4 && n_left_to_next >= 2) + { + u32 next0 = HICNPG_DATA_NEXT_DROP; + u32 next1 = HICNPG_DATA_NEXT_DROP; + u32 sw_if_index0, sw_if_index1; + + /* Prefetch next iteration. */ + { + vlib_buffer_t *p2, *p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + CLIB_PREFETCH (p2->data, (2 * CLIB_CACHE_LINE_BYTES), STORE); + CLIB_PREFETCH (p3->data, (2 * CLIB_CACHE_LINE_BYTES), STORE); + } + + /* + * speculatively enqueue b0 and b1 to the current + * next frame + */ + to_next[0] = bi0 = from[0]; + to_next[1] = bi1 = from[1]; + from += 2; + to_next += 2; + n_left_from -= 2; + n_left_to_next -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX]; + + /* Increment a counter */ + content_msgs_received += 2; + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE))) + { + if (b0->flags & VLIB_BUFFER_IS_TRACED) + { + icnpg_data_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->pkt_type = pkt_type0; + t->msg_type = msg_type0; + t->sw_if_index = sw_if_index0; + t->next_index = next0; + } + if (b1->flags & VLIB_BUFFER_IS_TRACED) + { + icnpg_data_trace_t *t = + vlib_add_trace (vm, node, b1, sizeof (*t)); + t->pkt_type = pkt_type1; + t->msg_type = msg_type1; + t->sw_if_index = sw_if_index1; + t->next_index = next1; + } + } + pkts_processed += 2; + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 next0 = HICNPG_DATA_NEXT_DROP; + u32 sw_if_index0; + + /* speculatively enqueue b0 to the current next frame */ + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + + /* Increment a counter */ + content_msgs_received++; + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + icnpg_data_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->pkt_type = pkt_type0; + t->msg_type = msg_type0; + t->sw_if_index = sw_if_index0; + t->next_index = next0; + } + pkts_processed++; + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, hicn_pg_data_node.index, + HICNPG_ERROR_PROCESSED, pkts_processed); + vlib_node_increment_counter (vm, hicn_pg_data_node.index, + HICNPG_ERROR_CONTENT_MSGS_RECEIVED, + content_msgs_received); + return (frame->n_vectors); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE(hicn_pg_data_node) = +{ + .function = hicnpg_client_data_node_fn, + .name = "hicnpg-data", + .vector_size = sizeof(u32), + .format_trace = format_hicnpg_data_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(hicnpg_error_strings), + .error_strings = hicnpg_error_strings, + .n_next_nodes = HICNPG_DATA_N_NEXT, + .next_nodes = + { + [HICNPG_DATA_NEXT_DROP] = "error-drop" + }, +}; +/* *INDENT-ON* */ + +/* + * End of packet-generator client node + */ + +/* + * Beginning of packet-generation server node + */ + +/* Registration struct for a graph node */ +vlib_node_registration_t hicn_pg_server_node; + +/* Stats, which end up called "error" even though they aren't... */ +#define foreach_icnpg_server_error \ +_(PROCESSED, "hICN PG Server packets processed") \ +_(DROPPED, "hICN PG Server packets dropped") + +typedef enum +{ +#define _(sym,str) HICNPG_SERVER_ERROR_##sym, + foreach_icnpg_server_error +#undef _ + HICNPG_SERVER_N_ERROR, +} icnpg_server_error_t; + +static char *icnpg_server_error_strings[] = { +#define _(sym,string) string, + foreach_icnpg_server_error +#undef _ +}; + +/* + * Next graph nodes, which reference the list in the actual registration + * block below + */ +typedef enum +{ + HICNPG_SERVER_NEXT_V4_LOOKUP, + HICNPG_SERVER_NEXT_V6_LOOKUP, + HICNPG_SERVER_NEXT_FACE_IP4_INPUT, + HICNPG_SERVER_NEXT_FACE_IP6_INPUT, + HICNPG_SERVER_NEXT_DROP, + HICNPG_SERVER_N_NEXT, +} icnpg_server_next_t; + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; + u16 msg_type; +} hicnpg_server_trace_t; + +/* packet trace format function */ +static u8 * +format_icnpg_server_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicnpg_server_trace_t *t = va_arg (*args, hicnpg_server_trace_t *); + + s = + format (s, + "HICNPG SERVER: pkt: %d, msg %d, sw_if_index %d, next index %d", + (int) t->pkt_type, (int) t->msg_type, t->sw_if_index, + t->next_index); + return (s); +} + +/* + * Node function for the icn packet-generator server. + */ +static uword +hicnpg_node_server_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next; + icnpg_server_next_t next_index; + u32 pkts_processed = 0, pkts_dropped = 0; + u32 bi0, bi1; + vlib_buffer_t *b0, *b1; + u8 pkt_type0 = 0, pkt_type1 = 0; + u16 msg_type0 = 0, msg_type1 = 0; + hicn_header_t *hicn0 = NULL, *hicn1 = NULL; + hicn_name_t name0, name1; + u16 namelen0, namelen1; + + hicnpg_server_main_t *hpgsm = &hicnpg_server_main; + + from = vlib_frame_vector_args (frame); + + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + + while (n_left_from >= 4 && n_left_to_next >= 2) + { + u32 next0 = HICNPG_SERVER_NEXT_DROP; + u32 next1 = HICNPG_SERVER_NEXT_DROP; + u8 isv6_0 = 0; + u8 isv6_1 = 0; + u32 sw_if_index0, sw_if_index1; + + /* Prefetch next iteration. */ + { + vlib_buffer_t *p2, *p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + CLIB_PREFETCH (p2->data, (2 * CLIB_CACHE_LINE_BYTES), STORE); + CLIB_PREFETCH (p3->data, (2 * CLIB_CACHE_LINE_BYTES), STORE); + } + + /* + * speculatively enqueue b0 and b1 to the current + * next frame + */ + to_next[0] = bi0 = from[0]; + to_next[1] = bi1 = from[1]; + from += 2; + to_next += 2; + n_left_from -= 2; + n_left_to_next -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX]; + + if (hicn_interest_parse_pkt (b0, &name0, &namelen0, &hicn0, &isv6_0) + == HICN_ERROR_NONE) + { + /* this node grabs only interests */ + vlib_buffer_t *rb = NULL; + rb = vlib_get_buffer (vm, hpgsm->pgen_svr_buffer_idx); + + isv6_0 ? convert_interest_to_data_v6 (vm, b0, rb, + bi0) : + convert_interest_to_data_v4 (vm, b0, rb, bi0); + + next0 = + isv6_0 ? HICNPG_SERVER_NEXT_V6_LOOKUP : + HICNPG_SERVER_NEXT_V4_LOOKUP; + /* if hicn_underneath ,the following will results as next0 = HICNPG_SERVER_NEXT_DATA_LOOKUP */ + next0 += 2 * hpgsm->hicn_underneath; + } + if (hicn_interest_parse_pkt (b1, &name1, &namelen1, &hicn1, &isv6_1) + == HICN_ERROR_NONE) + { + /* this node grabs only interests */ + vlib_buffer_t *rb = NULL; + rb = vlib_get_buffer (vm, hpgsm->pgen_svr_buffer_idx); + + isv6_1 ? convert_interest_to_data_v6 (vm, b1, rb, + bi1) : + convert_interest_to_data_v4 (vm, b1, rb, bi1); + + next1 = + isv6_1 ? HICNPG_SERVER_NEXT_V6_LOOKUP : + HICNPG_SERVER_NEXT_V4_LOOKUP; + /* if hicn_underneath ,the following will results as next0 = HICNPG_SERVER_NEXT_DATA_LOOKUP */ + next1 += 2 * hpgsm->hicn_underneath; + } + pkts_processed += 2; + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE))) + { + if (b0->flags & VLIB_BUFFER_IS_TRACED) + { + hicnpg_server_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->pkt_type = pkt_type0; + t->msg_type = msg_type0; + t->sw_if_index = sw_if_index0; + t->next_index = next0; + } + if (b1->flags & VLIB_BUFFER_IS_TRACED) + { + hicnpg_server_trace_t *t = + vlib_add_trace (vm, node, b1, sizeof (*t)); + t->pkt_type = pkt_type1; + t->msg_type = msg_type1; + t->sw_if_index = sw_if_index1; + t->next_index = next1; + } + } + if (next0 == HICNPG_SERVER_NEXT_DROP) + { + pkts_dropped++; + } + if (next1 == HICNPG_SERVER_NEXT_DROP) + { + pkts_dropped++; + } + /* + * verify speculative enqueues, maybe switch current + * next frame + */ + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 next0 = HICNPG_SERVER_NEXT_DROP; + u32 sw_if_index0 = ~0; + u8 isv6_0 = 0; + + /* speculatively enqueue b0 to the current next frame */ + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + + + if (hicn_interest_parse_pkt (b0, &name0, &namelen0, &hicn0, &isv6_0) + == HICN_ERROR_NONE) + { + /* this node grabs only interests */ + vlib_buffer_t *rb = NULL; + rb = vlib_get_buffer (vm, hpgsm->pgen_svr_buffer_idx); + + isv6_0 ? convert_interest_to_data_v6 (vm, b0, rb, + bi0) : + convert_interest_to_data_v4 (vm, b0, rb, bi0); + + next0 = + isv6_0 ? HICNPG_SERVER_NEXT_V6_LOOKUP : + HICNPG_SERVER_NEXT_V4_LOOKUP; + /* if hicn_underneath ,the following will results as next0 = HICNPG_SERVER_NEXT_DATA_LOOKUP */ + next0 += 2 * hpgsm->hicn_underneath; + } + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + hicnpg_server_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->pkt_type = pkt_type0; + t->msg_type = msg_type0; + t->sw_if_index = sw_if_index0; + t->next_index = next0; + } + pkts_processed += 1; + + if (next0 == HICNPG_SERVER_NEXT_DROP) + { + pkts_dropped++; + } + /* + * verify speculative enqueue, maybe switch current + * next frame + */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, hicn_pg_server_node.index, + HICNPG_SERVER_ERROR_PROCESSED, pkts_processed); + vlib_node_increment_counter (vm, hicn_pg_server_node.index, + HICNPG_SERVER_ERROR_DROPPED, pkts_dropped); + + return (frame->n_vectors); +} + +void +convert_interest_to_data_v4 (vlib_main_t * vm, vlib_buffer_t * b0, + vlib_buffer_t * rb, u32 bi0) +{ + hicn_header_t *h0 = vlib_buffer_get_current (b0); + + /* Get the packet length */ + u16 pkt_len = clib_net_to_host_u16 (h0->v4.ip.len); + + /* + * Rule of thumb: We want the size of the IP packet to be <= 1500 bytes + */ + u16 bytes_to_copy = rb->current_length; + if ((bytes_to_copy + pkt_len) > 1500) + { + bytes_to_copy = 1500 - pkt_len; + } + /* Add content to the data packet */ + vlib_buffer_add_data (vm, + VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX, &bi0, + rb->data, bytes_to_copy); + + b0 = vlib_get_buffer (vm, bi0); + + h0 = vlib_buffer_get_current (b0); + + ip4_address_t src_addr = h0->v4.ip.saddr; + h0->v4.ip.saddr = h0->v4.ip.daddr; + h0->v4.ip.daddr = src_addr; + + h0->v4.ip.len = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)); + h0->v4.ip.csum = ip4_header_checksum ((ip4_header_t *) & (h0->v4.ip)); + calculate_tcp_checksum_v4 (vm, b0); +} + +void +convert_interest_to_data_v6 (vlib_main_t * vm, vlib_buffer_t * b0, + vlib_buffer_t * rb, u32 bi0) +{ + hicn_header_t *h0 = vlib_buffer_get_current (b0); + + /* Get the packet length */ + uint16_t pkt_len = + clib_net_to_host_u16 (h0->v6.ip.len) + sizeof (ip6_header_t); + + /* + * Figure out how many bytes we can add to the content + * + * Rule of thumb: We want the size of the IP packet to be <= 1400 bytes + */ + u16 bytes_to_copy = rb->current_length; + if ((bytes_to_copy + pkt_len) > 1500) + { + bytes_to_copy = 1500 - pkt_len; + } + /* Add content to the data packet */ + vlib_buffer_add_data (vm, + VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX, &bi0, + rb->data, bytes_to_copy); + + b0 = vlib_get_buffer (vm, bi0); + + h0 = vlib_buffer_get_current (b0); + ip6_address_t src_addr = h0->v6.ip.saddr; + h0->v6.ip.saddr = h0->v6.ip.daddr; + h0->v6.ip.daddr = src_addr; + + h0->v6.ip.len = clib_host_to_net_u16 (vlib_buffer_length_in_chain + (vm, b0) - sizeof (ip6_header_t)); + h0->v6.tcp.data_offset_and_reserved |= 0x0f; + h0->v6.tcp.urg_ptr = htons (0xffff); + + calculate_tcp_checksum_v6 (vm, b0); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE(hicn_pg_server_node) = +{ + .function = hicnpg_node_server_fn, + .name = "hicnpg-server", + .vector_size = sizeof(u32), + .format_trace = format_icnpg_server_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(icnpg_server_error_strings), + .error_strings = icnpg_server_error_strings, + .n_next_nodes = HICNPG_SERVER_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = + { + [HICNPG_SERVER_NEXT_V4_LOOKUP] = "ip4-lookup", + [HICNPG_SERVER_NEXT_V6_LOOKUP] = "ip6-lookup", + [HICNPG_SERVER_NEXT_FACE_IP4_INPUT] = "hicn-face-ip4-input", + [HICNPG_SERVER_NEXT_FACE_IP6_INPUT] = "hicn-face-ip6-input", + [HICNPG_SERVER_NEXT_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +/* + * End of packet-generator server node + */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/pg.h b/hicn-plugin/src/pg.h new file mode 100755 index 000000000..083afb6b3 --- /dev/null +++ b/hicn-plugin/src/pg.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_PG_H__ +#define __HICN_PG_H__ + +/* Subnet-mask for punting data in the client node */ +#define SUBNET_MASK4 32 +#define SUBNET_MASK6 128 + +typedef struct hicnpg_main_s +{ + u32 index; + u32 index_ifaces; + u32 max_seq_number; + u32 n_flows; + u32 n_ifaces; + u32 hicn_underneath; + ip46_address_t pgen_clt_src_addr; + ip46_address_t pgen_clt_hicn_name; + u16 interest_lifetime; +} hicnpg_main_t; + +extern hicnpg_main_t hicnpg_main; + +typedef struct hicnpg_server_main_s +{ + u32 node_index; + u32 hicn_underneath; + /* Arbitrary content */ + u32 pgen_svr_buffer_idx; +} hicnpg_server_main_t; + +extern hicnpg_server_main_t hicnpg_server_main; + +#endif // __HICN_PG_H__ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/punt.c b/hicn-plugin/src/punt.c new file mode 100755 index 000000000..ea553bf76 --- /dev/null +++ b/hicn-plugin/src/punt.c @@ -0,0 +1,1005 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include // offsetof() +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hicn.h" +#include "infra.h" +#include "parser.h" +#include "mgmt.h" +#include "punt.h" +#include "error.h" +#include "route.h" + +/* Those are not static as they are used for pgen in hicn_cli.c */ +ip_version_t ipv4 = { + .tbl = (u32 *) hicn_punt_glb.ip4_vnet_tbl_idx, + .addr_len_bits = IPV4_ADDR_LEN_BITS, + .protocol_field = &ipv4_protocol, + .version_field = &ipv4_version, + .ip_version = 0x40, +}; + +ip_version_t ipv6 = { + .tbl = (u32 *) hicn_punt_glb.ip6_vnet_tbl_idx, + .addr_len_bits = IPV6_ADDR_LEN_BITS, + .protocol_field = &ipv6_protocol, + .version_field = &ipv6_version, + .ip_version = 0x60, +}; + +ip_version_t ipv44 = { + .tbl = (u32 *) hicn_punt_glb.udp44_vnet_tbl_idx, + .addr_len_bits = IPV4_ADDR_LEN_BITS, + .protocol_field = &udp4_protocol, + .udp_sport = &udp4_sport, + .udp_dport = &udp4_dport, + .ip_version = 0x40, +}; + +ip_version_t ipv64 = { + .tbl = (u32 *) hicn_punt_glb.udp64_vnet_tbl_idx, + .addr_len_bits = IPV4_ADDR_LEN_BITS, + .protocol_field = &udp6_protocol, + .udp_sport = &udp6_sport, + .udp_dport = &udp6_dport, + .ip_version = 0x60, +}; + +ip_version_t ipv46 = { + .tbl = (u32 *) hicn_punt_glb.udp46_vnet_tbl_idx, + .addr_len_bits = IPV6_ADDR_LEN_BITS, + .protocol_field = &udp4_protocol, + .udp_sport = &udp4_sport, + .udp_dport = &udp4_dport, + .ip_version = 0x40, +}; + +ip_version_t ipv66 = { + .tbl = (u32 *) hicn_punt_glb.udp66_vnet_tbl_idx, + .addr_len_bits = IPV6_ADDR_LEN_BITS, + .protocol_field = &udp6_protocol, + .udp_sport = &udp6_sport, + .udp_dport = &udp6_dport, + .ip_version = 0x60, +}; + +#define _(NAME, BASE, LAYER, FIELD, PUNT_ID) \ + field_t NAME = { \ + .offset = BASE + offsetof(LAYER, FIELD), \ + .len = STRUCT_SIZE_OF(LAYER, FIELD), \ + .punt_id = PUNT_ID, \ + }; +foreach_field +#undef _ +/* + * In the latest version, we let faces direct the traffic towards Interest + * processing, or MAP-Me nodes. Punting should only make sure that the ICMP + * packets are also sent to the face node. We added the following defines to + * determine the next node to send punted packets. Ideally we might remove + * protocol number check from punting rule. + */ +#define NEXT_MAPME_CTRL4 hicn_punt_glb.next_hit_interest_ipv4 +#define NEXT_MAPME_ACK4 hicn_punt_glb.next_hit_data_ipv4 +#define NEXT_MAPME_CTRL6 hicn_punt_glb.next_hit_interest_ipv6 +#define NEXT_MAPME_ACK6 hicn_punt_glb.next_hit_data_ipv6 + +/* Maximum number of vector allowed in match. Value hardcoded in vnet_classify_hash_packet_inline in vnet_classify.h */ +#define MAX_MATCH_SIZE 5 +/** + * HICN global Punt Info + * + * + * + */ +hicn_punt_glb_t hicn_punt_glb; + +/** + * We use the function build_bit_array to populate an initially empty buffer + * with masks/values for the parts of the packet to match. The function also + * returns the correct skip and match values to pass to vnet_classify_*, which + * are the number of vectors to skip/match during classification (they should be + * multiples of vector size = CLASSIFIER_VECTOR_SIZE). + * + * offsets: + * 0 14 offsetof(IP_HDR, SRC) + * | | / + * +----------+----+-------+-------+----+-... + * | ETH | IP . src . dst . | + * +----------+----+-------+-------+----+-... + * | | | + * |<- skip=1 ->|<--- match=2/3 --->| + * + * + */ + +/** + * The following section defines a couple of protocol fields that we will use + * for creating the buffer. We retrieve the offset and length on those fields + * based on the (portable) header struct aliases defined in libhicn. + * + * In the foreach_field macro, the punt_id field is used as convenience as we + * will have to create different classifier tables based on whether we punt + * interests (on dst) or data (on src). It is undefined (NA) otherwise. + */ + +#define NA 0 + + +/** + * @brief Create a bitmask from mask length. + * @param mask [in] mask length (in bits) + * @param buffer [out] output buffer + * @param len [out] output buffer length + */ +static void +build_ip_address_mask (u8 mask, u8 * buffer, u32 len) +{ + u32 hi_bytes = mask / 8; + u32 hi_bits = mask % 8; + u8 byte_mask = 0xff; + + /* + * memset buffer with 0xff in case of IPV6 16 bytes will be used for + * match + */ + memset (buffer, 0, len); + //might not be needed if buffer is already 0 'ed XXX + memset (buffer, 0xff, hi_bytes); + if (hi_bits != 0) + { + for (int i = 0; i < (8 - hi_bits); i++) + byte_mask = byte_mask << 1; + buffer[hi_bytes] = byte_mask; + } +} + +#define CEIL_DIV(x, y) (1 + ((x - 1) / y)) + +/** + * @brief Create a bit array from field/value list + * @param buffer [out] output buffer + * @param len [out] output buffer length + * @param skip [out] number of CLASSIFIER_VECTOR to skip + * @param match [out] number of CLASSIFIER_VECTOR to match + * @param ... [in] list of [field_t *, value] * used to populate buffer + */ +static int +build_bit_array (u8 * buffer, u32 len, u32 base_offset, u32 * skip, + u32 * match, va_list vl) +{ + u8 min = len, max = 0; + field_t *field; + u8 *value; + int pos; + int count = 0; + + /* Clear buffer */ + memset (buffer, 0, len); + + for (;;) + { + count++; + field = va_arg (vl, field_t *); + if (!field) + break; + + /* Check that the field belongs to the reserved buffer */ + if (field->offset + field->len > len) + goto ERR_PUNT; + + /* + * Copy the value of the field inside the buffer at the + * correct offset + */ + pos = base_offset + field->offset; + value = va_arg (vl, u8 *); + memcpy (buffer + pos, value, field->len); + if (min > pos) + min = pos; + if (max < pos + field->len) + max = pos + field->len; + } + + /* We can skip multiples of the vector match */ + *skip = min / CLASSIFIER_VECTOR_SIZE; + *match = CEIL_DIV (max, CLASSIFIER_VECTOR_SIZE) - *skip; + + if (*match > MAX_MATCH_SIZE) + *match = MAX_MATCH_SIZE; + + return HICN_ERROR_NONE; + +ERR_PUNT: + *skip = 0; + *match = 0; + return HICN_ERROR_PUNT_INVAL; +} + +void +update_table4_index (u32 intfc, u32 table_index) +{ + vnet_classify_main_t *cm = &vnet_classify_main; + + if (hicn_punt_glb.head_ip4[intfc] == ~0) + hicn_punt_glb.head_ip4[intfc] = table_index; + + /* Update the table in tail to poin to this */ + if (hicn_punt_glb.tail_ip4[intfc] != ~0) + { + vnet_classify_table_t *t = + pool_elt_at_index (cm->tables, hicn_punt_glb.tail_ip4[intfc]); + t->next_table_index = table_index; + } + hicn_punt_glb.tail_ip4[intfc] = table_index; +} + +void +update_table6_index (u32 intfc, u32 table_index) +{ + vnet_classify_main_t *cm = &vnet_classify_main; + + if (hicn_punt_glb.head_ip6[intfc] == ~0) + hicn_punt_glb.head_ip6[intfc] = table_index; + + /* Update the table in tail to poin to this */ + if (hicn_punt_glb.tail_ip6[intfc] != ~0) + { + vnet_classify_table_t *t = + pool_elt_at_index (cm->tables, hicn_punt_glb.tail_ip6[intfc]); + t->next_table_index = table_index; + } + hicn_punt_glb.tail_ip6[intfc] = table_index; +} + +/** + * @brief Add or remove a vnet table matching the list of fields/values passed + * as parameters. + * + * @param punt_id Storage identifier (HICN_PUNT_SRC | HICN_PUNT_DST) + * @param mask Subnet mask to match in the table + * @param next_tbl_index next table to match in case of miss + * @param intfc Interface identifier + * @param is_add 1 if the table must be created, 0 if removed + * @param ... list of (field_t, value) to be matched + * + * @result Returns: + * HICN_ERROR_TBL_EXIST if is_add == 1 and a table for the same mask + * already exists, + * HICN_ERROR_TBL_NOT_FOUND if is_add == 0 and there is no table for the + * given mask, + * HICN_ERROR_NONE if no * error occurred. + */ +int +_hicn_punt_add_del_vnettbl (ip_version_t * ip, u8 punt_id, u8 mask, + u32 next_tbl_index, u32 intfc, int base_offset, + int is_add, u8 use_current_data, ...) +{ + u8 buffer[PUNT_BUFFER_SIZE]; /* must be dimensioned + * large enough */ + int rt; + va_list vl; + u32 *table_index; + u32 new_table_index; + u32 skip, match; + + + /* Build the buffer right from the start to determine the skip size */ + va_start (vl, use_current_data); + build_bit_array (buffer, sizeof (buffer), base_offset, &skip, &match, vl); + va_end (vl); + + ASSERT (skip < 4); + //Hardcoded limit in following array + + table_index = TABLE_ELT_P (ip, intfc, skip, punt_id, mask); + + if (is_add && *table_index != HICNP_PUNY_INVALID_TBL) + return HICN_ERROR_PUNT_TBL_EXIST; + if (!is_add && *table_index == HICNP_PUNY_INVALID_TBL) + return HICN_ERROR_PUNT_TBL_NOT_FOUND; + + new_table_index = ~0; + rt = vnet_classify_add_del_table (&vnet_classify_main, + buffer + skip * CLASSIFIER_VECTOR_SIZE, + HICN_CLASSIFY_NBUCKETS, + HICN_CLASSIFY_TABLE_MEMORY_SIZE, skip, + match, HICN_CLASSIFY_NO_NEXT_TABLE, + HICN_CLASSIFY_MISS_NEXT_INDEX, + &new_table_index, + use_current_data, + HICN_CLASSIFY_CURRENT_DATA_OFFSET, is_add, + HICN_CLASSIFY_DON_T_DEL_CHAIN); + + if (rt != 0) + return HICN_ERROR_PUNT_INVAL; + + *table_index = new_table_index; + if (ip->ip_version == 0x40) + update_table4_index (intfc, new_table_index); + else + update_table6_index (intfc, new_table_index); + return HICN_ERROR_NONE; +} + +/** + * @brief Add or remove a vnet table matching the ip_version and field (src/dst) + */ +int +hicn_punt_add_del_vnettbl (ip_version_t * ip, field_t * field, u8 mask, + u32 next_tbl_index, u32 intfc, u8 base_offset, + u8 use_current_data, int is_add) +{ + u8 ip_mask[IPV6_ADDR_LEN]; + build_ip_address_mask (mask, ip_mask, sizeof (ip_mask)); + + return _hicn_punt_add_del_vnettbl (ip, field->punt_id, mask, next_tbl_index, + intfc, base_offset, is_add, + use_current_data, field, ip_mask, NULL); +} + + +/** + * @brief Add or remove a vnet table for udp tunnels matching the ip_version and field (src/dst) + * + */ +int +hicn_punt_add_del_vnettbl_udp (ip_version_t * outer, ip_version_t * inner, + field_t * field, u8 mask, u32 next_tbl_index, + u32 intfc, u8 base_offset, int is_add) +{ + u8 udp_mask[inner->addr_len_bits]; + build_ip_address_mask (mask, udp_mask, sizeof (udp_mask)); + u16 port_value = 0xffff; + u8 protocol_value = 0xff; + + return _hicn_punt_add_del_vnettbl (outer, field->punt_id, mask, + next_tbl_index, intfc, base_offset, + is_add, + HICN_CLASSIFY_NO_CURRENT_DATA_FLAG, + outer->protocol_field, &protocol_value, + outer->udp_sport, &port_value, + outer->udp_dport, &port_value, field, + udp_mask, NULL); +} + +#define hicn_punt_add_vnettbl_udp(outer, inner, field, mask, next_tbl_index, intfc, base_offset) \ + (hicn_punt_add_del_vnettbl_udp(outer, inner, field, mask, next_tbl_index, intfc, base_offset, OP_ADD)) + +#define hicn_punt_del_vnettbl_udp(outer, inner, field, mask, next_tbl_index, intfc, base_offset) \ + (hicn_punt_add_del_vnettbl_udp(outer, inner, field, mask, next_tbl_index, intfc, base_offset, OP_DEL)) + +/** + * @brief Add or remove a vnet session matching the list of fields/values passed + * as parameters. + * + * @param punt_id Storage identifier (HICN_PUNT_SRC | HICN_PUNT_DST) + * @param v4_address IPv4 address to match in the session // XXX v4/v6 + * @param mask Subnet mask to match in the session + * @param next_hit_index vlib arch id pointing to the next node + * @param intfc Interface identifier + * @param is_add 1 if the session must be create, 0 if removed + * @param ... list of (field_t, value) to be matched + * + * @result Returns: + * HICN_ERROR_TBL_NOT_FOUND there is no table for the given mask, + * HICN_ERROR_PUNT_SSN_NOT_FOUND if is_add == 0 and there is no session for + * the given address, + * HICN_ERROR_NONE if no error * occurred. + */ +int +_hicn_punt_add_del_vnetssn (ip_version_t * ip, u8 punt_id, u8 mask, + u32 next_hit_index, u32 intfc, int base_offset, + int is_add, ...) +{ + u8 buffer[PUNT_BUFFER_SIZE]; /* must be dimensioned + * large enough */ + int rt; + va_list vl; + u32 table_index; + u32 skip, match; + + /* Build the buffer right from the start to determine the skip size */ + va_start (vl, is_add); + build_bit_array (buffer, sizeof (buffer), base_offset, &skip, &match, vl); + va_end (vl); + + ASSERT (skip < 4); + //Hardcoded limit in following array + + table_index = TABLE_ELT (ip, intfc, skip, punt_id, mask); + + if (table_index == HICNP_PUNY_INVALID_TBL) + return HICN_ERROR_PUNT_TBL_NOT_FOUND; + + rt = vnet_classify_add_del_session (&vnet_classify_main, table_index, buffer, //+skip * CLASSIFIER_VECTOR_SIZE, + next_hit_index, + HICN_CLASSIFY_OPAQUE_INDEX, + HICN_CLASSIFY_ADVANCE, + HICN_CLASSIFY_ACTION, + HICN_CLASSIFY_METADATA, is_add); + + if (rt == VNET_API_ERROR_NO_SUCH_ENTRY) + rt = HICN_ERROR_PUNT_SSN_NOT_FOUND; + + return rt; +} + +/** + * @brief Add or remove a vnet session matching the ip6 src address + * + * See hicn_punt_add_del_vnetssn for details about parameters. + */ +int +hicn_punt_add_del_vnetssn (ip_version_t * ip, field_t * field, + ip46_address_t * v46_address, u8 mask, + u32 next_hit_index, u32 intfc, u8 base_offset, + int is_add) +{ + return _hicn_punt_add_del_vnetssn (ip, field->punt_id, mask, next_hit_index, + intfc, base_offset, is_add, field, + ip46_address_is_ip4 (v46_address) ? + v46_address->ip4.as_u8 : v46_address-> + ip6.as_u8, NULL); +} + + + +/** + * @brief Add or remove a vnet session for udp tunnels matching the ip6 src address + * + * See hicn_punt_add_del_vnetssn for details about parameters. + */ +int +hicn_punt_add_del_vnetssn_udp (ip_version_t * outer, ip_version_t * inner, + field_t * field, ip46_address_t * v46_address, + u8 mask, u32 next_hit_index, u32 intfc, + u8 base_offset, u8 protocol, u16 sport, + u16 dport, int is_add) +{ + return _hicn_punt_add_del_vnetssn (outer, field->punt_id, mask, + next_hit_index, intfc, base_offset, + is_add, outer->protocol_field, &protocol, + outer->udp_sport, &sport, + outer->udp_dport, &dport, field, + v46_address->as_u8, NULL); +} + +#define hicn_punt_add_vnetssn_udp(outer, inner, field, addr, mask, index, intfc, offset, protocol, sport, dport) \ + (hicn_punt_add_del_vnetssn_udp(outer, inner, field, addr, mask, index, intfc, offset, protocol, sport, dport, OP_ADD)) + +#define hicn_punt_del_vnetssn_udp(outer, inner, field, addr, mask, index, intfc, offset, protocol, sport, dport) \ + (hicn_punt_add_del_vnetssn_udp(outer, inner, field, addr, mask, index, intfc, offset, protocol, sport, dport, OP_DEL)) + +/* + * Enable the table on a given interface considering the table type + */ +void +hicn_punt_enable_disable_vnet_ip4_table_on_intf (vlib_main_t * vm, + u32 sw_if_index, + int is_enable) +{ + if (hicn_punt_glb.head_ip4[sw_if_index] != HICNP_PUNY_INVALID_TBL) + (void) vnet_set_input_acl_intfc (vm, sw_if_index, + hicn_punt_glb.head_ip4[sw_if_index], + 0xFFFFFFFF, 0xFFFFFFFF, is_enable); + return; +} + +/* + * Enable the table on a given interface considering the table type + * + * XXX replace skip by base_offset XXX are we sure we always have ETH_L2, and + * not base_offset ??? + */ +int +hicn_punt_remove_ip4_address (vlib_main_t * vm, ip4_address_t * addr, + u8 mask, int skip, u32 sw_if_index, + int is_enable) +{ + + vnet_classify_main_t *cm = &vnet_classify_main; + vnet_classify_table_t *vnet_table = NULL; + + u32 table_index = ~0; + + u32 base_offset = (skip ? ETH_L2 : NO_L2); + ip46_address_t addr46; + ip46_address_set_ip4 (&addr46, addr); + + hicn_punt_del_vnetssn (&ipv4, &ipv4_src, &addr46, mask, + hicn_punt_glb.next_hit_data_ipv4, sw_if_index, + ETH_L2); + hicn_punt_del_vnetssn (&ipv4, &ipv4_dst, &addr46, mask, + hicn_punt_glb.next_hit_interest_ipv4, sw_if_index, + ETH_L2); + + table_index = + hicn_punt_glb.ip4_vnet_tbl_idx[sw_if_index][skip][HICN_PUNT_DST][mask]; + vnet_table = pool_elt_at_index (cm->tables, table_index); + if (vnet_table->active_elements == 0) + { + hicn_punt_del_vnettbl (&ipv4, &ipv4_dst, mask, + hicn_punt_glb.ip4_vnet_tbl_idx[sw_if_index][skip] + [HICN_PUNT_SRC][mask], sw_if_index, base_offset); + } + table_index = + hicn_punt_glb.ip4_vnet_tbl_idx[sw_if_index][skip][HICN_PUNT_SRC][mask]; + vnet_table = pool_elt_at_index (cm->tables, table_index); + if (vnet_table->active_elements == 0) + { + hicn_punt_del_vnettbl (&ipv4, &ipv4_src, mask, ~0, sw_if_index, + base_offset); + } + return HICN_ERROR_NONE; +} + +int +hicn_punt_remove_ip6_address (vlib_main_t * vm, ip6_address_t * addr, + u8 mask, int skip, u32 sw_if_index, + int is_enable) +{ + + vnet_classify_main_t *cm = &vnet_classify_main; + vnet_classify_table_t *vnet_table = NULL; + + u32 table_index = ~0; + + u32 base_offset = (skip ? ETH_L2 : NO_L2); + + hicn_punt_del_vnetssn (&ipv6, &ipv6_src, (ip46_address_t *) addr, mask, + hicn_punt_glb.next_hit_data_ipv6, sw_if_index, + ETH_L2); + hicn_punt_del_vnetssn (&ipv6, &ipv6_dst, (ip46_address_t *) addr, mask, + hicn_punt_glb.next_hit_interest_ipv6, sw_if_index, + ETH_L2); + + table_index = + hicn_punt_glb.ip6_vnet_tbl_idx[sw_if_index][skip][HICN_PUNT_DST][mask]; + vnet_table = pool_elt_at_index (cm->tables, table_index); + if (vnet_table->active_elements == 0) + { + hicn_punt_del_vnettbl (&ipv6, &ipv6_dst, mask, + hicn_punt_glb.ip6_vnet_tbl_idx[sw_if_index][skip] + [HICN_PUNT_SRC][mask], sw_if_index, base_offset); + } + table_index = + hicn_punt_glb.ip6_vnet_tbl_idx[sw_if_index][skip][HICN_PUNT_SRC][mask]; + vnet_table = pool_elt_at_index (cm->tables, table_index); + if (vnet_table->active_elements == 0) + { + hicn_punt_del_vnettbl (&ipv6, &ipv6_src, mask, ~0, sw_if_index, + base_offset); + } + return HICN_ERROR_NONE; +} + +/* + * Enable the table on a given interface considering the table type + */ +void +hicn_punt_enable_disable_vnet_ip6_table_on_intf (vlib_main_t * vm, + u32 sw_if_index, + int is_enable) +{ + if (hicn_punt_glb.head_ip6[sw_if_index] != HICNP_PUNY_INVALID_TBL) + (void) vnet_set_input_acl_intfc (vm, sw_if_index, + 0xFFFFFFFF, + hicn_punt_glb.head_ip6[sw_if_index], + 0xFFFFFFFF, is_enable); + return; +} + +/* + * HICN PUNT vlibd node addtion + */ +void +hicn_punt_vlib_node_add (vlib_main_t * vm) +{ + u32 hit_next_index = 0xFFFFFFFF; + vlib_node_t *node; + + /* to remove the warning */ + hit_next_index = hit_next_index; + + //Accquire the node indexes + + /* ip face */ + node = vlib_get_node_by_name (vm, (u8 *) "hicn-face-ip4-input"); + hicn_punt_glb.hicn_node_info.hicn_face_ip4_input_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "hicn-face-ip6-input"); + hicn_punt_glb.hicn_node_info.hicn_face_ip6_input_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "hicn-face-ip4-output"); + hicn_punt_glb.hicn_node_info.hicn_face_ip4_output_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "hicn-face-ip6-output"); + hicn_punt_glb.hicn_node_info.hicn_face_ip6_output_index = node->index; + + /* ip iface */ + node = vlib_get_node_by_name (vm, (u8 *) "hicn-iface-ip4-input"); + hicn_punt_glb.hicn_node_info.hicn_iface_ip4_input_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "hicn-iface-ip6-input"); + hicn_punt_glb.hicn_node_info.hicn_iface_ip6_input_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "hicn-iface-ip4-output"); + hicn_punt_glb.hicn_node_info.hicn_iface_ip4_output_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "hicn-iface-ip6-output"); + hicn_punt_glb.hicn_node_info.hicn_iface_ip4_output_index = node->index; + + /* udp face */ + node = vlib_get_node_by_name (vm, (u8 *) "hicn-face-udp4-input"); + hicn_punt_glb.hicn_node_info.hicn_face_udp4_input_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "hicn-face-udp6-input"); + hicn_punt_glb.hicn_node_info.hicn_face_udp6_input_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "hicn-face-udp4-output"); + hicn_punt_glb.hicn_node_info.hicn_face_udp4_output_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "hicn-face-udp6-output"); + hicn_punt_glb.hicn_node_info.hicn_face_udp6_output_index = node->index; + + /* udp iface */ + node = vlib_get_node_by_name (vm, (u8 *) "hicn-iface-udp4-input"); + hicn_punt_glb.hicn_node_info.hicn_iface_udp4_input_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "hicn-iface-udp6-input"); + hicn_punt_glb.hicn_node_info.hicn_iface_udp6_input_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "hicn-iface-udp4-output"); + hicn_punt_glb.hicn_node_info.hicn_iface_udp4_output_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "hicn-iface-udp6-output"); + hicn_punt_glb.hicn_node_info.hicn_iface_udp6_output_index = node->index; + + node = vlib_get_node_by_name (vm, (u8 *) "ip4-inacl"); + hicn_punt_glb.hicn_node_info.ip4_inacl_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "ip6-inacl"); + hicn_punt_glb.hicn_node_info.ip6_inacl_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "ip4-lookup"); + hicn_punt_glb.hicn_node_info.ip4_lookup_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "ip6-lookup"); + hicn_punt_glb.hicn_node_info.ip6_lookup_node_index = node->index; + + + hicn_punt_glb.next_hit_data_ipv4 = vlib_node_add_next (vm, + hicn_punt_glb.hicn_node_info. + ip4_inacl_node_index, + hicn_punt_glb.hicn_node_info. + hicn_face_ip4_input_index); + + hicn_punt_glb.next_hit_interest_ipv4 = vlib_node_add_next (vm, + hicn_punt_glb.hicn_node_info. + ip4_inacl_node_index, + hicn_punt_glb.hicn_node_info. + hicn_iface_ip4_input_index); + + hicn_punt_glb.next_hit_data_ipv6 = vlib_node_add_next (vm, + hicn_punt_glb.hicn_node_info. + ip6_inacl_node_index, + hicn_punt_glb.hicn_node_info. + hicn_face_ip6_input_index); + + hicn_punt_glb.next_hit_interest_ipv6 = vlib_node_add_next (vm, + hicn_punt_glb.hicn_node_info. + ip6_inacl_node_index, + hicn_punt_glb.hicn_node_info. + hicn_iface_ip6_input_index); + + hicn_punt_glb.next_hit_data_udp4 = vlib_node_add_next (vm, + hicn_punt_glb.hicn_node_info. + ip4_inacl_node_index, + hicn_punt_glb.hicn_node_info. + hicn_face_udp4_input_index); + + hicn_punt_glb.next_hit_interest_udp4 = vlib_node_add_next (vm, + hicn_punt_glb.hicn_node_info. + ip4_inacl_node_index, + hicn_punt_glb.hicn_node_info. + hicn_iface_udp4_input_index); + + hicn_punt_glb.next_hit_data_udp6 = vlib_node_add_next (vm, + hicn_punt_glb.hicn_node_info. + ip6_inacl_node_index, + hicn_punt_glb.hicn_node_info. + hicn_face_udp6_input_index); + + hicn_punt_glb.next_hit_interest_udp6 = vlib_node_add_next (vm, + hicn_punt_glb.hicn_node_info. + ip6_inacl_node_index, + hicn_punt_glb.hicn_node_info. + hicn_iface_udp6_input_index); + + return; +} + +/* + * HICN PUNT INIT + */ +void +hicn_punt_init (vlib_main_t * vm) +{ + u32 table_index = ~0; + //Create vnet classify tables and store the table indexes + memset (hicn_punt_glb.ip4_vnet_tbl_idx, table_index, + sizeof (u32) * 4 * 2 * HICN_PUNT_IP4_MASK * HICN_MAX_INTFC); + memset (hicn_punt_glb.ip6_vnet_tbl_idx, table_index, + sizeof (u32) * 4 * 2 * HICN_PUNT_IP6_MASK * HICN_MAX_INTFC); + + memset (hicn_punt_glb.udp44_vnet_tbl_idx, table_index, + sizeof (u32) * 4 * 2 * HICN_PUNT_IP4_MASK * HICN_MAX_INTFC); + memset (hicn_punt_glb.udp46_vnet_tbl_idx, table_index, + sizeof (u32) * 4 * 2 * HICN_PUNT_IP6_MASK * HICN_MAX_INTFC); + memset (hicn_punt_glb.udp64_vnet_tbl_idx, table_index, + sizeof (u32) * 4 * 2 * HICN_PUNT_IP4_MASK * HICN_MAX_INTFC); + memset (hicn_punt_glb.udp66_vnet_tbl_idx, table_index, + sizeof (u32) * 4 * 2 * HICN_PUNT_IP6_MASK * HICN_MAX_INTFC); + //Register hicn nodes after vnet table creation + hicn_punt_vlib_node_add (vm); + memset (hicn_punt_glb.head_ip4, ~0, sizeof (u32) * HICN_MAX_INTFC); + memset (hicn_punt_glb.tail_ip4, ~0, sizeof (u32) * HICN_MAX_INTFC); + memset (hicn_punt_glb.head_ip6, ~0, sizeof (u32) * HICN_MAX_INTFC); + memset (hicn_punt_glb.tail_ip6, ~0, sizeof (u32) * HICN_MAX_INTFC); + return; +} + +u32 +hicn_punt_interest_data_for_udp (vlib_main_t * vm, + ip46_address_t * prefix, u8 mask, + u32 swif, u8 punt_type, u16 sport, u16 dport) +{ + int skip = 1; + u32 table_index; + + if (punt_type != HICN_PUNT_IP_TYPE && punt_type != HICN_PUNT_UDP4_TYPE + && punt_type != HICN_PUNT_UDP6_TYPE) + return HICN_ERROR_PUNT_INVAL; + + if (ip46_address_is_ip4 (prefix)) + { + if (mask > IPV4_ADDR_LEN_BITS) + return HICN_ERROR_PUNT_INVAL; + + if (punt_type == HICN_PUNT_UDP4_TYPE) + { + skip = 2; + /* Create Vnet table for a given mask */ + hicn_punt_add_vnettbl_udp (&ipv44, &ipv4, &udp44_src, mask, ~0, + swif, ETH_L2); + + table_index = + hicn_punt_glb.udp44_vnet_tbl_idx[swif][skip][HICN_PUNT_SRC][mask]; + + hicn_punt_add_vnettbl_udp (&ipv44, &ipv4, &udp44_dst, mask, + table_index, swif, ETH_L2); + /* + * Add a session for the specified ip address and + * subnet mask + */ + hicn_punt_add_vnetssn_udp (&ipv44, &ipv4, &udp44_src, + prefix, mask, + hicn_punt_glb.next_hit_data_udp4, + swif, ETH_L2, IPPROTO_UDP, sport, dport); + + hicn_punt_add_vnetssn_udp (&ipv44, &ipv4, &udp44_dst, + prefix, mask, + hicn_punt_glb.next_hit_interest_udp4, + swif, ETH_L2, IPPROTO_UDP, sport, dport); + + hicn_punt_enable_disable_vnet_ip4_table_on_intf (vm, swif, + OP_ENABLE); + } + else //PUNTING is UDP6 + { + skip = 3; + /* Create Vnet table for a given mask */ + hicn_punt_add_vnettbl_udp (&ipv64, &ipv6, &udp64_src, mask, ~0, + swif, ETH_L2); + + table_index = + hicn_punt_glb.udp64_vnet_tbl_idx[swif][skip][HICN_PUNT_SRC][mask]; + + hicn_punt_add_vnettbl_udp (&ipv64, &ipv6, &udp64_dst, mask, + table_index, swif, ETH_L2); + + /* + * Add a session for the specified ip address and + * subnet mask + */ + hicn_punt_add_vnetssn_udp (&ipv64, &ipv4, &udp64_src, + prefix, mask, + hicn_punt_glb.next_hit_data_udp6, + swif, ETH_L2, IPPROTO_UDP, sport, dport); + + hicn_punt_add_vnetssn_udp (&ipv64, &ipv4, &udp64_dst, + prefix, mask, + hicn_punt_glb.next_hit_interest_udp6, + swif, ETH_L2, IPPROTO_UDP, sport, dport); + + hicn_punt_enable_disable_vnet_ip6_table_on_intf (vm, swif, + OP_ENABLE); + } + } + else + { + if (punt_type == HICN_PUNT_UDP4_TYPE) + { + skip = 2; + /* Create Vnet table for a given mask */ + if (mask > 96) + return HICN_ERROR_PUNT_INVAL; + + hicn_punt_add_vnettbl_udp (&ipv46, &ipv4, &udp46_src, mask, ~0, + swif, ETH_L2); + + table_index = + hicn_punt_glb.udp46_vnet_tbl_idx[swif][skip][HICN_PUNT_SRC][mask]; + hicn_punt_add_vnettbl_udp (&ipv46, &ipv4, &udp46_dst, mask, + table_index, swif, ETH_L2); + + /* + * Add a session for the specified ip address and + * subnet mask + */ + hicn_punt_add_vnetssn_udp (&ipv46, &ipv4, &udp46_src, + prefix, mask, + hicn_punt_glb.next_hit_data_udp4, + swif, ETH_L2, IPPROTO_UDP, sport, dport); + hicn_punt_add_vnetssn_udp (&ipv46, &ipv4, &udp46_dst, + prefix, mask, + hicn_punt_glb.next_hit_interest_udp4, + swif, ETH_L2, IPPROTO_UDP, sport, dport); + + hicn_punt_enable_disable_vnet_ip4_table_on_intf (vm, swif, + OP_ENABLE); + } + else + { + if (mask > 122) + return HICN_ERROR_PUNT_INVAL; + + skip = 3; + hicn_punt_add_vnettbl_udp (&ipv66, &ipv6, &udp66_src, mask, ~0, + swif, ETH_L2); + + table_index = + hicn_punt_glb.udp66_vnet_tbl_idx[swif][skip][HICN_PUNT_SRC][mask]; + hicn_punt_add_vnettbl_udp (&ipv66, &ipv6, &udp66_dst, mask, + table_index, swif, ETH_L2); + + /* + * Add a session for the specified ip address and + * subnet mask + */ + hicn_punt_add_vnetssn_udp (&ipv66, &ipv6, &udp66_src, + prefix, mask, + hicn_punt_glb.next_hit_data_udp6, + swif, ETH_L2, IPPROTO_UDP, sport, dport); + hicn_punt_add_vnetssn_udp (&ipv66, &ipv6, &udp66_dst, + prefix, mask, + hicn_punt_glb.next_hit_interest_udp6, + swif, ETH_L2, IPPROTO_UDP, sport, dport); + + hicn_punt_enable_disable_vnet_ip6_table_on_intf (vm, swif, + OP_ENABLE); + } + + } + return HICN_ERROR_NONE; +} + + + +u32 +hicn_punt_interest_data_for_ethernet (vlib_main_t * vm, + ip46_address_t * prefix, u8 mask, + u32 swif, u8 punt_type) +{ + int skip = 1; + u32 table_index; + u8 use_current_data = HICN_CLASSIFY_NO_CURRENT_DATA_FLAG; + + if (punt_type != HICN_PUNT_IP_TYPE && punt_type != HICN_PUNT_UDP4_TYPE + && punt_type != HICN_PUNT_UDP6_TYPE) + return HICN_ERROR_PUNT_INVAL; + + if (ip46_address_is_ip4 (prefix)) + { + if (mask > IPV4_ADDR_LEN_BITS) + return HICN_ERROR_PUNT_INVAL; + + if (punt_type == HICN_PUNT_IP_TYPE) + { + /* Create Vnet table for a given mask */ + hicn_punt_add_vnettbl (&ipv4, &ipv4_src, mask, ~0, swif, ETH_L2, + use_current_data); + + table_index = + hicn_punt_glb.ip4_vnet_tbl_idx[swif][skip][HICN_PUNT_SRC][mask]; + + hicn_punt_add_vnettbl (&ipv4, &ipv4_dst, mask, table_index, swif, + ETH_L2, use_current_data); + + /* + * Add a session for the specified ip address and + * subnet mask + */ + hicn_punt_add_vnetssn (&ipv4, &ipv4_src, + prefix, mask, + hicn_punt_glb.next_hit_data_ipv4, swif, + ETH_L2); + hicn_punt_add_vnetssn (&ipv4, &ipv4_dst, + prefix, mask, + hicn_punt_glb.next_hit_interest_ipv4, swif, + ETH_L2); + + hicn_punt_enable_disable_vnet_ip4_table_on_intf (vm, swif, + OP_ENABLE); + } + else + { + return HICN_ERROR_PUNT_INVAL; + } + } + else + { + if (punt_type == HICN_PUNT_IP_TYPE) + { + if (mask > IPV6_ADDR_LEN_BITS) + return HICN_ERROR_PUNT_INVAL; + + /* Create Vnet table for a given mask */ + hicn_punt_add_vnettbl (&ipv6, &ipv6_src, mask, ~0, swif, ETH_L2, + use_current_data); + + table_index = + hicn_punt_glb.ip6_vnet_tbl_idx[swif][skip][HICN_PUNT_SRC][mask]; + + hicn_punt_add_vnettbl (&ipv6, &ipv6_dst, mask, table_index, swif, + ETH_L2, use_current_data); + + /* + * Add a session for the specified ip address and + * subnet mask + */ + hicn_punt_add_vnetssn (&ipv6, &ipv6_src, prefix, + mask, hicn_punt_glb.next_hit_data_ipv6, swif, + ETH_L2); + hicn_punt_add_vnetssn (&ipv6, &ipv6_dst, prefix, + mask, hicn_punt_glb.next_hit_interest_ipv6, + swif, ETH_L2); + + hicn_punt_enable_disable_vnet_ip6_table_on_intf (vm, swif, + OP_ENABLE); + } + else + { + return HICN_ERROR_PUNT_INVAL; + } + + } + return HICN_ERROR_NONE; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/punt.h b/hicn-plugin/src/punt.h new file mode 100755 index 000000000..ebc27e9d4 --- /dev/null +++ b/hicn-plugin/src/punt.h @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_PUNT_H__ +#define __HICN_PUNT_H__ + +#include +#include + +#define HICN_CLASSIFY_TABLE_MEMORY_SIZE (2*1024*1024) // 2MB allocated for the classification table +#define HICN_PUNTING_BUFFER_SIZE_32 (32) +#define HICN_PUNTING_BUFFER_SIZE_48 (48) +#define HICN_PUNTING_BUFFER_SIZE_64 (64) +#define HICN_PUNTING_BUFFER_SIZE_80 (80) +#define HICN_PUNTING_BUFFER_SIZE_128 (128) + +/* Limits */ + +#define HICN_PUNT_IP4 0 +#define HICN_PUNT_IP6 1 + +#define HICN_MAX_INTFC 256 + +/* We also consider mask = 0 to match everything */ +#define HICN_PUNT_IP4_MASK 33 +#define HICN_PUNT_IP6_MASK 129 + +#define HICN_PUNT_IP_TYPE 0 +#define HICN_PUNT_UDP4_TYPE 1 +#define HICN_PUNT_UDP6_TYPE 2 +/* + * u32 ip4_vnet_tbl_idx[HICN_MAX_INTFC][2][3][HICN_PUNT_IP4_MASK]; + * //[skip][src][mask],[skip][dst][mask] u32 + * ip6_vnet_tbl_idx[HICN_MAX_INTFC][2][3][HICN_PUNT_IP6_MASK]; + * //[skip][src][mask],[skip][dst][mask] + */ +#define PUNT_MASK(ip) (ip->addr_len_bits + 1) +#define TABLE_ELT_P(ip, i, j, k, l) (ip->tbl + (4 * 2 * PUNT_MASK(ip)) * i + (2 * PUNT_MASK(ip)) * j + k * PUNT_MASK(ip) + l) +#define TABLE_ELT(ip, i, j, k, l) (*(TABLE_ELT_P(ip, i, j, k, l))) + +#define NO_L2 0 +#define ETH_L2 sizeof(ethernet_header_t) + +#define IPPROTO_MASK 0xFF + +/* Index to access vnet table index */ +#define HICN_PUNT_SRC 0 +#define HICN_PUNT_DST 1 + +#define HICN_PUNT_OK 0 +#define HICN_PUNT_ERR 1 + +#define HICNP_PUNY_INVALID_TBL ~0 + +/* Number of bytes before the next header/protocol field in ip6/4 */ +#define BYTES_TO_PROTOCOL_IP4 9 +#define BYTES_TO_NEXT_HEADER_IP6 6 + +#define PUNT_BUFFER_SIZE 100 /* B */ +#define CLASSIFIER_VECTOR_SIZE 16 /* B */ + +#define OP_DEL 0 +#define OP_ADD 1 +#define OP_DISABLE 0 +#define OP_ENABLE 1 + +/* vnet_classify_add_del_table */ +#define HICN_CLASSIFY_NO_NEXT_TABLE 0xFFFFFFFF +#define HICN_CLASSIFY_MISS_NEXT_INDEX 16 +#define HICN_CLASSIFY_CURRENT_DATA_FLAG CLASSIFY_FLAG_USE_CURR_DATA +#define HICN_CLASSIFY_NO_CURRENT_DATA_FLAG 0 +#define HICN_CLASSIFY_CURRENT_DATA_OFFSET 0 +#define HICN_CLASSIFY_DON_T_DEL_CHAIN 0 + +/* vnet_classify_add_del_session */ +#define HICN_CLASSIFY_OPAQUE_INDEX 0xFFFFFFFF +#define HICN_CLASSIFY_ADVANCE 0 +#define HICN_CLASSIFY_ACTION 0 +#define HICN_CLASSIFY_METADATA 0 + +/* This should be equal to the number of rules we expect in each table */ +#define HICN_CLASSIFY_NBUCKETS 3 + + +/* HICN punt node index */ +typedef struct _hicn_node_info_s +{ + u32 hicn_face_ip4_input_index; + u32 hicn_face_ip6_input_index; + u32 hicn_iface_ip4_input_index; + u32 hicn_iface_ip6_input_index; + u32 hicn_face_ip4_output_index; + u32 hicn_face_ip6_output_index; + u32 hicn_iface_ip4_output_index; + u32 hicn_iface_ip6_output_index; + u32 hicn_face_udp4_input_index; + u32 hicn_face_udp6_input_index; + u32 hicn_iface_udp4_input_index; + u32 hicn_iface_udp6_input_index; + u32 hicn_face_udp4_output_index; + u32 hicn_face_udp6_output_index; + u32 hicn_iface_udp4_output_index; + u32 hicn_iface_udp6_output_index; + u32 ip4_inacl_node_index; + u32 ip6_inacl_node_index; + u32 ip4_lookup_node_index; + u32 ip6_lookup_node_index; +} hicn_node_info_t; + +/* + * HICN global PUNT info + */ +typedef struct _hicn_punt_glb_s +{ + hicn_node_info_t hicn_node_info; + + /* + * The following nodes are used to create the vlib node graph, and + * point classified packets to the right node. + */ + u32 next_hit_interest_ipv4; + //node - graph index to forward packets to our hicn nodes + u32 next_hit_data_ipv4; + u32 next_hit_interest_ipv6; + //node - graph index to forward packets to our hicn nodes + u32 next_hit_data_ipv6; + u32 next_hit_interest_udp4; + //node - graph index to forward packets to our hicn nodes + u32 next_hit_data_udp4; + u32 next_hit_interest_udp6; + //node - graph index to forward packets to our hicn nodes + u32 next_hit_data_udp6; + + /* + * One table is created : - per interface : so that we can have + * different punted prefixes per interface, and thus decrease the + * amount of matched rules per packet. An interface will be + * consistently receiving packets with or without the ethernet + * header, and thus the offsets should always be correct. - per skip + * (assuming it is for the base offset (ethernet or not), in which + * case the interface should be sufficient. - per prefix length to + * allow for sorting later. - per src / dst (?) + * + * Note that there is no test on the packet type (v4 or v6), as they + * follow distinct paths in the vpp graph and will thus be dispatched + * to distinct classifiers. This is also why we duplicate the state + * for both IPv4 and IPv6 in this implementation. + * + * Tables are chained per interface in the order they are added. Each + * table consists in a set of rules (named sessions). + * + * / interface --> table i [.next_table_index=j] --> table j [.nti=~0] + * -- drop \ | | +-- on match, + * send to node m +-- [...] to node n + * + * For debugging purposes, you can use the following commands: + * + * vppctl show inacl type ip4 vppctl show inacl type ip6 + * + * vppctl show classify tables [verbose] + * + * TODO: - allow tables to be removed - sort tables with decreasing + * prefix length to allow for LPM. - directly access the linked list + * through vpp APIs and remove global variables. They are not + * sufficient anyways for removal. + */ + + /** + * Given the current implementation, the following multidimensional array + * stores the table indexes uniquerly identified by the 4-tuple (interface, + * skip, src/dst, mask). + * + * For flexibility, some macros and functions will be defined in the .c to + * manipulate this array. + */ + u32 ip4_vnet_tbl_idx[HICN_MAX_INTFC][4][2][HICN_PUNT_IP4_MASK]; + //[skip][src][mask],[skip][dst][mask] + u32 ip6_vnet_tbl_idx[HICN_MAX_INTFC][4][2][HICN_PUNT_IP6_MASK]; + //[skip][src][mask],[skip][dst][mask] + u32 udp44_vnet_tbl_idx[HICN_MAX_INTFC][4][2][HICN_PUNT_IP4_MASK]; + //[skip][src][mask],[skip][dst][mask] + u32 udp46_vnet_tbl_idx[HICN_MAX_INTFC][4][2][HICN_PUNT_IP6_MASK]; + //[skip][src][mask],[skip][dst][mask] + u32 udp64_vnet_tbl_idx[HICN_MAX_INTFC][4][2][HICN_PUNT_IP4_MASK]; + //[skip][src][mask],[skip][dst][mask] + u32 udp66_vnet_tbl_idx[HICN_MAX_INTFC][4][2][HICN_PUNT_IP6_MASK]; + //[skip][src][mask],[skip][dst][mask] + + /* + * The first and last tables associated to each interface (both for + * v4 and v6) are stored. They are respectively used to : - start + * classification on the correct table depending on the input + * interface: the assumption is that different interfaces with punt + * different prefixes, which should decreate the number of potential + * rules to match for each incoming packet. see. + * vnet_set_input_acl_intfc() - maintain the chaining between tables + * so that upon addition, the newly created table can be chained to + * the previous last one. + */ + u32 head_ip4[HICN_MAX_INTFC]; + u32 tail_ip4[HICN_MAX_INTFC]; + u32 head_ip6[HICN_MAX_INTFC]; + u32 tail_ip6[HICN_MAX_INTFC]; + +} hicn_punt_glb_t; + +extern hicn_punt_glb_t hicn_punt_glb; + + + +/* XXX The two following structs might be opaque */ + +#define NA 0 + +typedef struct +{ + u32 offset; + u32 len; /* bytes */ + u32 punt_id; /* see explanation in hicn_punt.c */ +} field_t; + +/* Format: _(name, base, layer, field, punt_id) */ +#define foreach_field \ + _(ipv6_src, 0, _ipv6_header_t, saddr, HICN_PUNT_SRC) \ + _(ipv6_dst, 0, _ipv6_header_t, daddr, HICN_PUNT_DST) \ + _(ipv6_protocol, 0, _ipv6_header_t, nxt, NA) \ + _(ipv4_src, 0, _ipv4_header_t, saddr, HICN_PUNT_SRC) \ + _(ipv4_dst, 0, _ipv4_header_t, daddr, HICN_PUNT_DST) \ + _(ipv4_protocol, 0, _ipv4_header_t, protocol, NA) \ + \ + _(ipv4_version, 0, _ipv4_header_t, version_ihl, NA) \ + _(ipv6_version, 0, _ipv6_header_t, vfc, NA) \ + _(udp4_sport, IPV4_HDRLEN, _udp_header_t, src_port, NA) \ + _(udp4_dport, IPV4_HDRLEN, _udp_header_t, dst_port, NA) \ + _(udp6_sport, IPV6_HDRLEN, _udp_header_t, src_port, NA) \ + _(udp6_dport, IPV6_HDRLEN, _udp_header_t, dst_port, NA) \ + _(udp6_protocol, 0, _ipv6_header_t, nxt, NA) \ + _(udp4_protocol, 0, _ipv4_header_t, protocol, NA) \ + _(udp46_src, IPV4_HDRLEN + UDP_HDRLEN, _ipv6_header_t, saddr, HICN_PUNT_SRC) \ + _(udp46_dst, IPV4_HDRLEN + UDP_HDRLEN, _ipv6_header_t, daddr, HICN_PUNT_DST) \ + _(udp44_src, IPV4_HDRLEN + UDP_HDRLEN, _ipv4_header_t, saddr, HICN_PUNT_SRC) \ + _(udp44_dst, IPV4_HDRLEN + UDP_HDRLEN, _ipv4_header_t, daddr, HICN_PUNT_DST) \ + _(udp66_src, IPV6_HDRLEN + UDP_HDRLEN, _ipv6_header_t, saddr, HICN_PUNT_SRC) \ + _(udp66_dst, IPV6_HDRLEN + UDP_HDRLEN, _ipv6_header_t, daddr, HICN_PUNT_DST) \ + _(udp64_src, IPV6_HDRLEN + UDP_HDRLEN, _ipv6_header_t, saddr, HICN_PUNT_SRC) \ + _(udp64_dst, IPV6_HDRLEN + UDP_HDRLEN, _ipv6_header_t, daddr, HICN_PUNT_DST) \ + + +#define _(NAME, BASE, LAYER, FIELD, PUNT_ID) \ + extern field_t NAME; +foreach_field +#undef _ + typedef struct +{ + u32 *tbl; + u8 addr_len_bits; + field_t *protocol_field; + field_t *version_field; + field_t *udp_sport; + field_t *udp_dport; + u8 ip_version; +} ip_version_t; + +extern ip_version_t ipv4; +extern ip_version_t ipv6; + + +/* ------------------------- */ + +/** + * @brief Punt table APIs + * + * Those APIs are called when the first punting table is created for a given + * interface, so as to point to the start of the chain. + */ +void +hicn_punt_enable_disable_vnet_ip4_table_on_intf (vlib_main_t * vm, + u32 sw_if_index, + int is_enable); +void +hicn_punt_enable_disable_vnet_ip6_table_on_intf (vlib_main_t * vm, + u32 sw_if_index, + int is_enable); +u32 hicn_punt_interest_data_for_udp (vlib_main_t * vm, + ip46_address_t * prefix, u8 mask, + u32 swif, u8 punt_type, u16 sport, + u16 dport); +u32 hicn_punt_interest_data_for_ethernet (vlib_main_t * vm, + ip46_address_t * prefix, u8 mask, + u32 swif, u8 type); +int hicn_punt_remove_ip6_address (vlib_main_t * vm, ip6_address_t * addr, + u8 mask, int skip, u32 swif, int is_enable); +int hicn_punt_remove_ip4_address (vlib_main_t * vm, ip4_address_t * addr, + u8 mask, int skip, u32 swif, int is_enable); +void hicn_punt_init (vlib_main_t * vm); + +int +hicn_punt_add_del_vnettbl (ip_version_t * ip, field_t * field, u8 mask, u32 + next_tbl_index, u32 intfc, u8 base_offset, + u8 use_current_data, int is_add); + +#define hicn_punt_add_vnettbl(ip, field, mask, next_tbl_index, intfc, base_offset, use_current_data) \ + (hicn_punt_add_del_vnettbl(ip, field, mask, next_tbl_index, intfc, base_offset, use_current_data, OP_ADD)) + +#define hicn_punt_del_vnettbl(ip, field, mask, next_tbl_index, intfc, base_offset) \ + (hicn_punt_add_del_vnettbl(ip, field, mask, next_tbl_index, intfc, base_offset, HICN_CLASSIFY_NO_CURRENT_DATA_FLAG, OP_DEL)) + +int +hicn_punt_add_del_vnetssn (ip_version_t * ip, field_t * field, + ip46_address_t * v46_address, u8 mask, + u32 next_hit_index, u32 intfc, u8 base_offset, + int is_add); + +#define hicn_punt_add_vnetssn(ip, field, addr, mask, index, intfc, offset) \ + (hicn_punt_add_del_vnetssn(ip, field, addr, mask, index, intfc, offset, OP_ADD)) + +#define hicn_punt_del_vnetssn(ip, field, addr, mask, index, intfc, offset) \ + (hicn_punt_add_del_vnetssn(ip, field, addr, mask, index, intfc, offset, OP_DEL)) + +#endif /* // __HICN_PUNT_H__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/route.c b/hicn-plugin/src/route.c new file mode 100755 index 000000000..9202efbd4 --- /dev/null +++ b/hicn-plugin/src/route.c @@ -0,0 +1,392 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include "strategy_dpo_ctx.h" +#include "strategy_dpo_manager.h" +#include "strategy.h" +#include "faces/face.h" +#include "error.h" +#include "strategies/dpo_mw.h" + +int +hicn_route_get_dpo (const ip46_address_t * prefix, u8 plen, + const dpo_id_t ** hicn_dpo, u32 * fib_index) +{ + fib_prefix_t fib_pfx; + const dpo_id_t *load_balance_dpo_id; + const dpo_id_t *former_dpo_id; + int found = 0, ret = HICN_ERROR_ROUTE_NOT_FOUND; + fib_node_index_t fib_entry_index; + + /* At this point the face exists in the face table */ + fib_prefix_from_ip46_addr (prefix, &fib_pfx); + fib_pfx.fp_len = plen; + + + /* Check if the route already exist in the fib */ + /* + * ASSUMPTION: we use table 0 which is the default table and it is + * already existing and locked + */ + *fib_index = fib_table_find_or_create_and_lock (fib_pfx.fp_proto, + HICN_FIB_TABLE, + FIB_SOURCE_PLUGIN_HI); + fib_entry_index = fib_table_lookup_exact_match (*fib_index, &fib_pfx); + + if (fib_entry_index != FIB_NODE_INDEX_INVALID) + { + /* Route already existing. We need to update the dpo. */ + load_balance_dpo_id = + fib_entry_contribute_ip_forwarding (fib_entry_index); + + /* The dpo is not a load balance dpo as expected */ + if (load_balance_dpo_id->dpoi_type != DPO_LOAD_BALANCE) + ret = HICN_ERROR_ROUTE_NO_LD; + else + { + /* former_dpo_id is a load_balance dpo */ + load_balance_t *lb = + load_balance_get (load_balance_dpo_id->dpoi_index); + + /* FIB entry exists but there is no hicn dpo. */ + ret = HICN_ERROR_ROUTE_DPO_NO_HICN; + for (int i = 0; i < lb->lb_n_buckets && !found; i++) + { + former_dpo_id = load_balance_get_bucket_i (lb, i); + + if (dpo_is_hicn (former_dpo_id)) + { + *hicn_dpo = former_dpo_id; + ret = HICN_ERROR_NONE; + found = 1; + } + } + } + } + /* + * Remove the lock from the table. We keep one lock per route, not + * per dpo + */ + fib_table_unlock (*fib_index, fib_pfx.fp_proto, FIB_SOURCE_PLUGIN_HI); + + return ret; +} + +/* Add a new route for a name prefix */ +int +hicn_route_add (hicn_face_id_t * face_id, u32 len, + const ip46_address_t * prefix, u8 plen) +{ + + fib_prefix_t fib_pfx; + dpo_id_t dpo = DPO_INVALID; + const dpo_id_t *hicn_dpo_id; + int ret = HICN_ERROR_NONE; + dpo_id_t face_dpo_tmp[HICN_PARAM_FIB_ENTRY_NHOPS_MAX]; + int n_face_dpo = 0; + index_t dpo_idx; + u32 fib_index; + vlib_main_t *vm = vlib_get_main (); + hicn_face_vft_t *face_vft = NULL; + + if (face_id == NULL) + { + return HICN_ERROR_ROUTE_INVAL; + } + /* + * Check is the faces are available, otherwise skip the face + * id_adjacency existance is not checked. It should be checked before + * sending a packet out + */ + for (int i = 0; i < clib_min (HICN_PARAM_FIB_ENTRY_NHOPS_MAX, len); i++) + { + hicn_face_t *face = hicn_dpoi_get_from_idx (face_id[i]); + face_vft = hicn_face_get_vft (face->shared.face_type); + dpo_id_t face_dpo = DPO_INVALID; + face_vft->hicn_face_get_dpo (face, &face_dpo); + + if (!dpo_id_is_valid (&face_dpo)) + { + vlib_cli_output (vm, "Face %d not found, skip...\n", face_id[i]); + return ret; + } + else + { + face_dpo_tmp[n_face_dpo++] = face_dpo; + } + } + + ret = hicn_route_get_dpo (prefix, plen, &hicn_dpo_id, &fib_index); + + if (ret == HICN_ERROR_ROUTE_NOT_FOUND) + { + /* The Fib entry does not exist */ + /* At this point the face exists in the face table */ + fib_prefix_from_ip46_addr (prefix, &fib_pfx); + fib_pfx.fp_len = plen; + + dpo_id_t nhops[HICN_PARAM_FIB_ENTRY_NHOPS_MAX]; + for (int i = 0; i < n_face_dpo; i++) + { + clib_memcpy (&nhops[i], &face_dpo_tmp[i], sizeof (dpo_id_t)); + } + + ret = + default_dpo.hicn_dpo_create (fib_pfx.fp_proto, nhops, n_face_dpo, + &dpo_idx); + + if (ret) + { + return ret; + } + /* the value we got when we registered */ + /* + * This should be taken from the name?!? the index of the + * object + */ + dpo_set (&dpo, + default_dpo.hicn_dpo_get_type (), + (ip46_address_is_ip4 (prefix) ? DPO_PROTO_IP4 : DPO_PROTO_IP6), + dpo_idx); + + /* Here is where we create the "via" like route */ + /* + * For the moment we use the global one the prefix you want + * to match Neale suggested -- FIB_SOURCE_HICN the client + * that is adding them -- no easy explanation at this time… + */ + fib_node_index_t new_fib_node_index = + fib_table_entry_special_dpo_add (fib_index, + &fib_pfx, + FIB_SOURCE_PLUGIN_HI, + FIB_ENTRY_FLAG_EXCLUSIVE, + &dpo); + + /* We added a route, therefore add one lock to the table */ + fib_table_lock (fib_index, fib_pfx.fp_proto, FIB_SOURCE_PLUGIN_HI); + + dpo_unlock (&dpo); + ret = + (new_fib_node_index != + FIB_NODE_INDEX_INVALID) ? HICN_ERROR_NONE : + HICN_ERROR_ROUTE_NO_INSERT; + + /* + * TODO: we might want to store the fib index in the face. + * This will help to update the fib entries when a face is + * deleted. Fib_index_t is returned from + * fib_table_entry_special_dpo_add. + */ + } + else if (ret == HICN_ERROR_NONE) + { + ret = HICN_ERROR_ROUTE_ALREADY_EXISTS; + } + return ret; +} + +int +hicn_route_add_nhops (hicn_face_id_t * face_id, u32 len, + const ip46_address_t * prefix, u8 plen) +{ + const dpo_id_t *hicn_dpo_id; + int ret = HICN_ERROR_NONE; + dpo_id_t faces_dpo_tmp[HICN_PARAM_FIB_ENTRY_NHOPS_MAX]; + int n_face_dpo = 0; + const hicn_dpo_vft_t *dpo_vft; + u32 fib_index; + vlib_main_t *vm = vlib_get_main (); + hicn_face_vft_t *face_vft = NULL; + + if (face_id == NULL) + { + return HICN_ERROR_ROUTE_INVAL; + } + /* + * Check is the faces are available, otherwise skip the face + * id_adjacency existance is not checked. It should be checked before + * sending a packet out + */ + for (int i = 0; i < clib_min (HICN_PARAM_FIB_ENTRY_NHOPS_MAX, len); i++) + { + hicn_face_t *face = hicn_dpoi_get_from_idx (face_id[i]); + face_vft = hicn_face_get_vft (face->shared.face_type); + dpo_id_t face_dpo = DPO_INVALID; + face_vft->hicn_face_get_dpo (face, &face_dpo); + + if (!dpo_id_is_valid (&face_dpo)) + { + vlib_cli_output (vm, "Face %d not found, skip...\n", face_id[i]); + return ret; + } + else + { + faces_dpo_tmp[n_face_dpo++] = face_dpo; + } + } + + ret = hicn_route_get_dpo (prefix, plen, &hicn_dpo_id, &fib_index); + + if (ret == HICN_ERROR_NONE) + { + for (int i = 0; i < n_face_dpo && (ret == HICN_ERROR_NONE); i++) + { + u32 vft_id = hicn_dpo_get_vft_id (hicn_dpo_id); + dpo_vft = hicn_dpo_get_vft (vft_id); + ret = dpo_vft->hicn_dpo_add_update_nh (&faces_dpo_tmp[i], + hicn_dpo_id->dpoi_index); + } + } + return ret; +} + +int +hicn_route_del (ip46_address_t * prefix, u8 plen) +{ + fib_prefix_t fib_pfx; + const dpo_id_t *hicn_dpo_id; + int ret = HICN_ERROR_NONE; + u32 fib_index; + + /* At this point the face exists in the face table */ + fib_prefix_from_ip46_addr (prefix, &fib_pfx); + fib_pfx.fp_len = plen; + + /* Remove the fib entry only if the dpo is of type hicn */ + ret = hicn_route_get_dpo (prefix, plen, &hicn_dpo_id, &fib_index); + + if (ret == HICN_ERROR_NONE) + { + fib_table_entry_special_remove (HICN_FIB_TABLE, &fib_pfx, + FIB_SOURCE_PLUGIN_HI); + + /* + * Remove the lock from the table. We keep one lock per route + */ + fib_table_unlock (fib_index, fib_pfx.fp_proto, FIB_SOURCE_PLUGIN_HI); + } + //Remember to remove the lock from the table when removing the entry + return ret; +} + +int +hicn_route_del_nhop (ip46_address_t * prefix, u8 plen, hicn_face_id_t face_id) +{ + + fib_prefix_t fib_pfx; + const dpo_id_t *hicn_dpo_id; + int ret; + u32 vft_id; + const hicn_dpo_vft_t *dpo_vft; + u32 fib_index; + + /* At this point the face exists in the face table */ + fib_prefix_from_ip46_addr (prefix, &fib_pfx); + fib_pfx.fp_len = plen; + + ret = hicn_route_get_dpo (prefix, plen, &hicn_dpo_id, &fib_index); + + /* Check if the dpo is an hicn_dpo_t */ + if (ret == HICN_ERROR_NONE) + { + vft_id = hicn_dpo_get_vft_id (hicn_dpo_id); + dpo_vft = hicn_dpo_get_vft (vft_id); + return dpo_vft->hicn_dpo_del_nh (face_id, hicn_dpo_id->dpoi_index, + &fib_pfx); + } + //Remember to remove the lock from the table when removing the entry + return ret; +} + +int +hicn_route_set_strategy (ip46_address_t * prefix, u8 plen, u8 strategy_id) +{ + fib_prefix_t fib_pfx; + const dpo_id_t *hicn_dpo_id; + dpo_id_t new_dpo_id = DPO_INVALID; + int ret; + hicn_dpo_ctx_t *old_hicn_dpo_ctx; + const hicn_dpo_vft_t *old_dpo_vft; + const hicn_dpo_vft_t *new_dpo_vft; + index_t new_hicn_dpo_idx; + u32 fib_index; + u32 old_vft_id; + + /* At this point the face exists in the face table */ + fib_prefix_from_ip46_addr (prefix, &fib_pfx); + fib_pfx.fp_len = plen; + + ret = hicn_route_get_dpo (prefix, plen, &hicn_dpo_id, &fib_index); + + if (ret == HICN_ERROR_NONE) + { + old_vft_id = hicn_dpo_get_vft_id (hicn_dpo_id); + old_dpo_vft = hicn_dpo_get_vft (old_vft_id); + old_hicn_dpo_ctx = + old_dpo_vft->hicn_dpo_get_ctx (hicn_dpo_id->dpoi_index); + + new_dpo_vft = hicn_dpo_get_vft_from_id (strategy_id); + + if (new_dpo_vft == NULL) + return HICN_ERROR_STRATEGY_NOT_FOUND; + + /* Create a new dpo for the new strategy */ + new_dpo_vft->hicn_dpo_create (hicn_dpo_id->dpoi_proto, + old_hicn_dpo_ctx->next_hops, + old_hicn_dpo_ctx->entry_count, + &new_hicn_dpo_idx); + + /* the value we got when we registered */ + dpo_set (&new_dpo_id, + new_dpo_vft->hicn_dpo_get_type (), + (ip46_address_is_ip4 (prefix) ? DPO_PROTO_IP4 : + DPO_PROTO_IP6), new_hicn_dpo_idx); + + /* Here is where we create the "via" like route */ + /* + * For the moment we use the global one the prefix you want + * to match Neale suggested -- FIB_SOURCE_HICN the client + * that is adding them -- no easy explanation at this time… + */ + fib_node_index_t new_fib_node_index = + fib_table_entry_special_dpo_update (fib_index, + &fib_pfx, + FIB_SOURCE_PLUGIN_HI, + FIB_ENTRY_FLAG_EXCLUSIVE, + &new_dpo_id); + + dpo_unlock (&new_dpo_id); + ret = + (new_fib_node_index != + FIB_NODE_INDEX_INVALID) ? HICN_ERROR_NONE : + HICN_ERROR_ROUTE_NOT_UPDATED; + } + //Remember to remove the lock from the table when removing the entry + return ret; + +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/route.h b/hicn-plugin/src/route.h new file mode 100755 index 000000000..be15b9906 --- /dev/null +++ b/hicn-plugin/src/route.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_ROUTE__ +#define __HICN_ROUTE__ + +#include +#include +#include "hicn.h" +#include "faces/face.h" + +/* + * Retrieve the hicn dpo corresponding to a hicn prefix + */ +int +hicn_route_get_dpo (const ip46_address_t * prefix, u8 plen, + const dpo_id_t ** hicn_dpo, u32 * fib_index); + +/* + * Add a new route for a name prefix + */ +int +hicn_route_add (hicn_face_id_t * face_id, u32 len, + const ip46_address_t * prefix, u8 plen); + +/* + * Add new next hops for a prefix route + */ +int +hicn_route_add_nhops (hicn_face_id_t * face_id, u32 len, + const ip46_address_t * prefix, u8 plen); + +/* Remove a route for a name prefix */ +int hicn_route_del (ip46_address_t * prefix, u8 plen); + +/* Remove a next hop route for a name prefix */ +int hicn_route_del_nhop (ip46_address_t * prefix, u8 plen, u32 face_id); + +/* Remove a next hop route for a name prefix */ +int +hicn_route_set_strategy (ip46_address_t * prefix, u8 plen, u32 strategy_id); + +#endif /* //__HICN_ROUTE__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/state.h b/hicn-plugin/src/state.h new file mode 100755 index 000000000..7e984e6c3 --- /dev/null +++ b/hicn-plugin/src/state.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_STATE__ +#define __HICN_STATE__ + +#include +#include + +#include "hicn.h" +#include "pcs.h" +#include "hashtb.h" +#include "strategy.h" +#include "strategy_dpo_ctx.h" +#include "strategy_dpo_manager.h" + +always_inline void +hicn_prefetch_pcs_entry (hicn_buffer_t * hicnb, hicn_pit_cs_t * pitcs) +{ + hicn_hash_node_t *node = pool_elt_at_index (pitcs->pcs_table->ht_nodes, + hicnb->node_id); + + hicn_hash_bucket_t *bucket; + if (hicnb->hash_bucket_flags & HICN_HASH_NODE_OVERFLOW_BUCKET) + bucket = + pool_elt_at_index (pitcs->pcs_table->ht_overflow_buckets, + hicnb->bucket_id); + else + bucket = + (hicn_hash_bucket_t *) (pitcs->pcs_table->ht_buckets + + hicnb->bucket_id); + + CLIB_PREFETCH (node, CLIB_CACHE_LINE_BYTES, STORE); + CLIB_PREFETCH (bucket, CLIB_CACHE_LINE_BYTES, STORE); +} + +always_inline void +hicn_get_internal_state (hicn_buffer_t * hicnb, hicn_pit_cs_t * pitcs, + hicn_hash_node_t ** node, + const hicn_strategy_vft_t ** strategy_vft, + const hicn_dpo_vft_t ** dpo_vft, u8 * dpo_ctx_id, + hicn_hash_entry_t ** hash_entry) +{ + *node = pool_elt_at_index (pitcs->pcs_table->ht_nodes, hicnb->node_id); + *strategy_vft = hicn_dpo_get_strategy_vft (hicnb->vft_id); + *dpo_vft = hicn_dpo_get_vft (hicnb->vft_id); + *dpo_ctx_id = hicnb->dpo_ctx_id; + + hicn_hash_bucket_t *bucket; + if (hicnb->hash_bucket_flags & HICN_HASH_NODE_OVERFLOW_BUCKET) + bucket = + pool_elt_at_index (pitcs->pcs_table->ht_overflow_buckets, + hicnb->bucket_id); + else + bucket = + (hicn_hash_bucket_t *) (pitcs->pcs_table->ht_buckets + + hicnb->bucket_id); + + *hash_entry = &(bucket->hb_entries[hicnb->hash_entry_id]); +} + +/* + * This function set the PCS entry index, the dpo index and the vft index in + * the opaque2 buffer. In this way, the interest-hitpit and interest-hitcs + * nodes can prefetch the corresponding state (PIT entry, dpo_ctx and the + * strategy vft + */ +always_inline void +hicn_store_internal_state (vlib_buffer_t * b, u64 name_hash, u32 node_id, + u8 dpo_ctx_id, u8 vft_id, u8 hash_entry_id, + u32 bucket_id, u8 bucket_is_overflow) +{ + hicn_buffer_t *hicnb = hicn_get_buffer (b); + hicnb->name_hash = name_hash; + hicnb->node_id = node_id; + hicnb->dpo_ctx_id = dpo_ctx_id; + hicnb->vft_id = vft_id; + hicnb->hash_entry_id = hash_entry_id; + hicnb->bucket_id = bucket_id; + hicnb->hash_bucket_flags = + HICN_HASH_NODE_OVERFLOW_BUCKET * bucket_is_overflow; +} + +#endif /* // __HICN_STATE__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/strategies/dpo_mw.c b/hicn-plugin/src/strategies/dpo_mw.c new file mode 100755 index 000000000..882368e6e --- /dev/null +++ b/hicn-plugin/src/strategies/dpo_mw.c @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "../strategy_dpo_ctx.h" +#include "dpo_mw.h" +#include "strategy_mw.h" +#include "../strategy_dpo_manager.h" + +hicn_strategy_mw_ctx_t *hicn_strategy_mw_ctx_pool; + +const static char *const hicn_ip6_nodes[] = { + "hicn-mw-strategy", // this is the name you give your node in VLIB_REGISTER_NODE + NULL, +}; + +const static char *const hicn_ip4_nodes[] = { + "hicn-mw-strategy", // this is the name you give your node in VLIB_REGISTER_NODE + NULL, +}; + +const static char *const *const hicn_nodes_mw[DPO_PROTO_NUM] = { + [DPO_PROTO_IP6] = hicn_ip6_nodes, + [DPO_PROTO_IP4] = hicn_ip4_nodes, +}; + +/** + * @brief DPO type value for the mw_strategy + */ +static dpo_type_t hicn_dpo_type_mw; + +static const hicn_dpo_vft_t hicn_dpo_mw_vft = { + .hicn_dpo_get_ctx = &hicn_strategy_mw_ctx_get, + .hicn_dpo_is_type = &hicn_dpo_is_type_strategy_mw, + .hicn_dpo_get_type = &hicn_dpo_strategy_mw_get_type, + .hicn_dpo_module_init = &hicn_dpo_strategy_mw_module_init, + .hicn_dpo_create = &hicn_strategy_mw_ctx_create, + .hicn_dpo_add_update_nh = &hicn_strategy_mw_ctx_add_nh, + .hicn_dpo_del_nh = &hicn_strategy_mw_ctx_del_nh, + .hicn_dpo_lock_dpo_ctx = &hicn_strategy_mw_ctx_lock, + .hicn_dpo_unlock_dpo_ctx = hicn_strategy_mw_ctx_unlock, + .format_hicn_dpo = &format_hicn_dpo_strategy_mw +}; + +int +hicn_dpo_is_type_strategy_mw (const dpo_id_t * dpo) +{ + return dpo->dpoi_type == hicn_dpo_type_mw; +} + +void +hicn_dpo_strategy_mw_module_init (void) +{ + pool_validate_index (hicn_strategy_mw_ctx_pool, 0); + /* + * Register our type of dpo + */ + hicn_dpo_type_mw = + hicn_dpo_register_new_type (hicn_nodes_mw, &hicn_dpo_mw_vft, + hicn_mw_strategy_get_vft (), + &dpo_strategy_mw_ctx_vft); +} + +u8 * +format_hicn_dpo_strategy_mw (u8 * s, va_list * ap) +{ + + u32 indent = va_arg (*ap, u32); + s = + format (s, + "Static Weights: weights are updated by the control plane, next hop is the one with the maximum weight.\n", + indent); + return (s); +} + +dpo_type_t +hicn_dpo_strategy_mw_get_type (void) +{ + return hicn_dpo_type_mw; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// + +void +hicn_strategy_mw_ctx_lock (dpo_id_t * dpo) +{ + hicn_strategy_mw_ctx_t *hicn_strategy_mw_ctx = + (hicn_strategy_mw_ctx_t *) hicn_strategy_mw_ctx_get (dpo->dpoi_index); + hicn_strategy_mw_ctx->default_ctx.locks++; +} + +void +hicn_strategy_mw_ctx_unlock (dpo_id_t * dpo) +{ + hicn_strategy_mw_ctx_t *hicn_strategy_mw_ctx = + (hicn_strategy_mw_ctx_t *) hicn_strategy_mw_ctx_get (dpo->dpoi_index); + hicn_strategy_mw_ctx->default_ctx.locks--; + + if (0 == hicn_strategy_mw_ctx->default_ctx.locks) + { + pool_put (hicn_strategy_mw_ctx_pool, hicn_strategy_mw_ctx); + } +} + +u8 * +format_hicn_strategy_mw_ctx (u8 * s, va_list * ap) +{ + int i = 0; + index_t index = va_arg (*ap, index_t); + hicn_strategy_mw_ctx_t *dpo = NULL; + dpo_id_t *next_hop = NULL; + hicn_face_vft_t *face_vft = NULL; + u32 indent = va_arg (*ap, u32);; + + dpo = (hicn_strategy_mw_ctx_t *) hicn_strategy_mw_ctx_get (index); + + s = format (s, "hicn-mw"); + for (i = 0; i < HICN_PARAM_FIB_ENTRY_NHOPS_MAX; i++) + { + next_hop = &dpo->default_ctx.next_hops[i]; + face_vft = hicn_face_get_vft (next_hop->dpoi_type); + if (face_vft != NULL) + { + s = format (s, "\n"); + s = + format (s, "%U ", face_vft->format_face, next_hop->dpoi_index, + indent); + s = format (s, "weight %u", dpo->weight[i]); + } + } + + return (s); +} + +static index_t +hicn_strategy_mw_ctx_get_index (hicn_strategy_mw_ctx_t * cd) +{ + return (cd - hicn_strategy_mw_ctx_pool); +} + +int +hicn_strategy_mw_ctx_create (dpo_proto_t proto, const dpo_id_t * next_hop, + int nh_len, index_t * dpo_idx) +{ + hicn_strategy_mw_ctx_t *hicn_strategy_mw_ctx; + int ret = HICN_ERROR_NONE, i; + dpo_id_t invalid = NEXT_HOP_INVALID; + + /* Allocate a hicn_dpo_ctx on the vpp pool and initialize it */ + pool_get (hicn_strategy_mw_ctx_pool, hicn_strategy_mw_ctx); + + *dpo_idx = hicn_strategy_mw_ctx_get_index (hicn_strategy_mw_ctx); + for (int i = 0; i < HICN_PARAM_FIB_ENTRY_NHOPS_MAX; i++) + { + hicn_strategy_mw_ctx->default_ctx.next_hops[i] = invalid; + } + + hicn_strategy_mw_ctx->default_ctx.entry_count = 0; + hicn_strategy_mw_ctx->default_ctx.locks = 0; + + for (i = 0; i < HICN_PARAM_FIB_ENTRY_NHOPS_MAX && i < nh_len; i++) + { + clib_memcpy (&hicn_strategy_mw_ctx->default_ctx.next_hops[i], + &next_hop[i], sizeof (dpo_id_t)); + hicn_strategy_mw_ctx->default_ctx.entry_count++; + } + + memset (hicn_strategy_mw_ctx->weight, 0, HICN_PARAM_FIB_ENTRY_NHOPS_MAX); + + return ret; +} + +hicn_dpo_ctx_t * +hicn_strategy_mw_ctx_get (index_t index) +{ + hicn_strategy_mw_ctx_t *hicn_strategy_mw_ctx = NULL; + if (!pool_is_free_index (hicn_strategy_mw_ctx_pool, index)) + { + hicn_strategy_mw_ctx = + (pool_elt_at_index (hicn_strategy_mw_ctx_pool, index)); + } + return &hicn_strategy_mw_ctx->default_ctx; +} + +int +hicn_strategy_mw_ctx_add_nh (const dpo_id_t * nh, index_t dpo_idx) +{ + hicn_strategy_mw_ctx_t *hicn_strategy_mw_ctx = + (hicn_strategy_mw_ctx_t *) hicn_strategy_mw_ctx_get (dpo_idx); + + if (hicn_strategy_mw_ctx != NULL) + { + + int empty = hicn_strategy_mw_ctx->default_ctx.entry_count; + + /* Iterate through the list of faces to add new faces */ + for (int i = 0; i < hicn_strategy_mw_ctx->default_ctx.entry_count; i++) + { + if (!memcmp + (nh, &hicn_strategy_mw_ctx->default_ctx.next_hops[i], + sizeof (dpo_id_t))) + { + /* If face is marked as deleted, ignore it */ + hicn_face_t *face = + hicn_dpoi_get_from_idx (hicn_strategy_mw_ctx-> + default_ctx.next_hops[i].dpoi_index); + if (face->shared.flags & HICN_FACE_FLAGS_DELETED) + { + continue; + } + return HICN_ERROR_DPO_CTX_NHOPS_EXISTS; + } + } + + /* Get an empty place */ + if (empty > HICN_PARAM_FIB_ENTRY_NHOPS_MAX) + { + return HICN_ERROR_DPO_CTX_NHOPS_NS; + } + if (PREDICT_FALSE (empty > HICN_PARAM_FIB_ENTRY_NHOPS_MAX)) + { + return HICN_ERROR_DPO_CTX_NHOPS_NS; + } + clib_memcpy (&hicn_strategy_mw_ctx->default_ctx.next_hops[empty], nh, + sizeof (dpo_id_t)); + hicn_strategy_mw_ctx->default_ctx.entry_count++; + + return HICN_ERROR_NONE; + } + return HICN_ERROR_DPO_CTX_NOT_FOUND; +} + +int +hicn_strategy_mw_ctx_del_nh (hicn_face_id_t face_id, index_t dpo_idx, + fib_prefix_t * fib_pfx) +{ + hicn_strategy_mw_ctx_t *hicn_strategy_mw_ctx = + (hicn_strategy_mw_ctx_t *) hicn_strategy_mw_ctx_get (dpo_idx); + int ret = HICN_ERROR_NONE; + int nh_id = ~0; + dpo_id_t invalid = NEXT_HOP_INVALID; + + if (hicn_strategy_mw_ctx != NULL) + { + for (int i = 0; i < hicn_strategy_mw_ctx->default_ctx.entry_count; i++) + { + if (hicn_strategy_mw_ctx->default_ctx.next_hops[i].dpoi_index == + face_id) + { + nh_id = i; + hicn_face_unlock (&hicn_strategy_mw_ctx->default_ctx. + next_hops[i]); + hicn_strategy_mw_ctx->default_ctx.next_hops[i] = invalid; + hicn_strategy_mw_ctx->default_ctx.entry_count--; + } + } + + if (0 == hicn_strategy_mw_ctx->default_ctx.entry_count) + { + fib_table_entry_special_remove (HICN_FIB_TABLE, fib_pfx, + FIB_SOURCE_PLUGIN_HI); + } + } + else + { + ret = HICN_ERROR_DPO_CTX_NOT_FOUND; + } + + /* + * Remove any possible hole in the arrays of dpos + */ + if (hicn_strategy_mw_ctx->default_ctx.entry_count > 0 && nh_id != ~0 + && nh_id < hicn_strategy_mw_ctx->default_ctx.entry_count - 1) + { + int i; + for (i = nh_id; i < hicn_strategy_mw_ctx->default_ctx.entry_count; i++) + { + clib_memcpy (&hicn_strategy_mw_ctx->default_ctx.next_hops[i], + &hicn_strategy_mw_ctx->default_ctx.next_hops[i + 1], + sizeof (dpo_id_t)); + } + /* Set as invalid the last dpo */ + hicn_strategy_mw_ctx->default_ctx.next_hops[i] = invalid; + } + return ret; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/strategies/dpo_mw.h b/hicn-plugin/src/strategies/dpo_mw.h new file mode 100755 index 000000000..a8c0a3b43 --- /dev/null +++ b/hicn-plugin/src/strategies/dpo_mw.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_DPO_MW_H__ +#define __HICN_DPO_MW_H__ + +#include +#include "../strategy_dpo_ctx.h" + +typedef struct hicn_strategy_mw_ctx_s +{ + hicn_dpo_ctx_t default_ctx; + + u8 weight[HICN_PARAM_FIB_ENTRY_NHOPS_MAX]; +} hicn_strategy_mw_ctx_t; + +/** + * @brief Lock the mw ctx + * + * @param dpo Identifier of the dpo of the mw ctx + */ +void hicn_strategy_mw_ctx_lock (dpo_id_t * dpo); + +/** + * @brief Unlock the mw ctx + * + * @param dpo Identifier of the dpo of the mw ctx + */ +void hicn_strategy_mw_ctx_unlock (dpo_id_t * dpo); + +/** + * @brief Format the dpo ctx for a human-readable string + * + * @param s String to which to append the formatted dpo ctx + * @param ap List of parameters for the formatting + * + * @result The string with the formatted dpo ctx + */ +u8 *format_hicn_strategy_mw_ctx (u8 * s, va_list * ap); + +const static dpo_vft_t dpo_strategy_mw_ctx_vft = { + .dv_lock = hicn_strategy_mw_ctx_lock, + .dv_unlock = hicn_strategy_mw_ctx_unlock, + .dv_format = format_hicn_strategy_mw_ctx, +}; + +/** + * @brief Retrieve an hicn_strategy_mw_ctx object + * + * @param indext Index of the hicn_dpo_ctx to retrieve + * @return The hicn_dpo_ctx object or NULL + */ +hicn_dpo_ctx_t *hicn_strategy_mw_ctx_get (index_t index); + +/** + * @brief Create a new mw ctx + * + * @param proto The protocol to which the dpo is meant for (see vpp docs) + * @param next_hop A list of next hops to be inserted in the dpo ctx + * @param nh_len Size of the list + * @param dpo_idx index_t that will hold the index of the created dpo ctx + * @return HICN_ERROR_NONE if the creation was fine, otherwise EINVAL + */ +int +hicn_strategy_mw_ctx_create (dpo_proto_t proto, const dpo_id_t * next_hop, + int nh_len, index_t * dpo_idx); + +/** + * @brief Add or update a next hop in the dpo ctx. + * + * This function is meant to be used in the control plane and not in the data plane, + * as it is not optimized for the latter. + * + * @param nh Next hop to insert in the dpo ctx + * @param dpo_idx Index of the dpo ctx to update with the new or updated next + * hop + * @return HICN_ERROR_NONE if the update or insert was fine, + * otherwise HICN_ERROR_DPO_CTX_NOT_FOUND + */ +int hicn_strategy_mw_ctx_add_nh (const dpo_id_t * nh, index_t dpo_idx); + +/** + * @brief Delete a next hop in the dpo ctx. + * + * @param face_id Face identifier of the next hop + * @param dpo_idx Index of the dpo ctx to update with the new or updated next + * hop + * @return HICN_ERROR_NONE if the update or insert was fine, + * otherwise HICN_ERROR_DPO_CTS_NOT_FOUND + */ +int +hicn_strategy_mw_ctx_del_nh (hicn_face_id_t face_id, index_t dpo_idx, + fib_prefix_t * fib_pfx); + +/** + * @brief Prefetch a dpo + * + * @param dpo_idx Index of the dpo ctx to prefetch + */ +void hicn_strategy_mw_ctx_prefetch (index_t dpo_idx); + +int hicn_dpo_is_type_strategy_mw (const dpo_id_t * dpo); + +void hicn_dpo_strategy_mw_module_init (void); + +dpo_type_t hicn_dpo_strategy_mw_get_type (void); + +u8 *format_hicn_dpo_strategy_mw (u8 * s, va_list * ap); + + +#endif // __HICN_DPO_MW_H__ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/strategies/strategy_mw.c b/hicn-plugin/src/strategies/strategy_mw.c new file mode 100755 index 000000000..144dd145e --- /dev/null +++ b/hicn-plugin/src/strategies/strategy_mw.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "../strategy.h" +#include "../strategy_dpo_ctx.h" +#include "dpo_mw.h" +#include "../faces/face.h" +#include "../route.h" +#include "../pcs.h" +#include "../strategy_dpo_manager.h" + +/* Simple strategy that chooses the next hop with the maximum weight */ +/* It does not require to exend the hicn_dpo */ +void hicn_receive_data_mw (index_t dpo_idx, int nh_idx); +void hicn_add_interest_mw (index_t dpo_idx, hicn_hash_entry_t * pit_entry); +void hicn_on_interest_timeout_mw (index_t dpo_idx); +u32 hicn_select_next_hop_mw (index_t dpo_idx, int *nh_idx, + dpo_id_t ** outface); +u32 get_strategy_node_index_mw (void); + +static hicn_strategy_vft_t hicn_strategy_mw_vft = { + .hicn_receive_data = &hicn_receive_data_mw, + .hicn_add_interest = &hicn_add_interest_mw, + .hicn_on_interest_timeout = &hicn_on_interest_timeout_mw, + .hicn_select_next_hop = &hicn_select_next_hop_mw, + .get_strategy_node_index = get_strategy_node_index_mw +}; + +/* Stats string values */ +static char *hicn_strategy_error_strings[] = { +#define _(sym, string) string, + foreach_hicnfwd_error +#undef _ +}; + +/* + * Return the vft of the strategy. + */ +hicn_strategy_vft_t * +hicn_mw_strategy_get_vft (void) +{ + return &hicn_strategy_mw_vft; +} + +/* Registration struct for a graph node */ +vlib_node_registration_t hicn_mw_strategy_node; + +u32 +get_strategy_node_index_mw (void) +{ + return hicn_mw_strategy_node.index; +} + +/* DPO should be give in input as it containes all the information to calculate the next hops*/ +u32 +hicn_select_next_hop_mw (index_t dpo_idx, int *nh_idx, dpo_id_t ** outface) +{ + hicn_strategy_mw_ctx_t *hicn_strategy_mw_ctx = + (hicn_strategy_mw_ctx_t *) hicn_strategy_mw_ctx_get (dpo_idx); + + u8 next_hop_index = 0; + for (int i = 0; i < HICN_PARAM_FIB_ENTRY_NHOPS_MAX; i++) + { + if (dpo_id_is_valid (&hicn_strategy_mw_ctx->default_ctx.next_hops[i])) + { + if (hicn_strategy_mw_ctx->weight[next_hop_index] < + hicn_strategy_mw_ctx->weight[i]) + { + next_hop_index = i; + } + } + } + + if (!dpo_id_is_valid + (&hicn_strategy_mw_ctx->default_ctx.next_hops[next_hop_index])) + return HICN_ERROR_MW_STRATEGY_NH_NOT_FOUND; + + *outface = + (dpo_id_t *) & hicn_strategy_mw_ctx->default_ctx. + next_hops[next_hop_index]; + + return HICN_ERROR_NONE; +} + +uword +hicn_mw_strategy_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return hicn_forward_interest_fn (vm, node, frame, &hicn_strategy_mw_vft, + &hicn_mw_strategy_node); +} + +void +hicn_add_interest_mw (index_t dpo_ctx_idx, hicn_hash_entry_t * hash_entry) +{ + hash_entry->dpo_ctx_id = dpo_ctx_idx; + dpo_id_t hicn_dpo_id = + { hicn_dpo_strategy_mw_get_type (), 0, 0, dpo_ctx_idx }; + hicn_strategy_mw_ctx_lock (&hicn_dpo_id); + hash_entry->vft_id = hicn_dpo_get_vft_id (&hicn_dpo_id); +} + +void +hicn_on_interest_timeout_mw (index_t dpo_idx) +{ + /* Nothign to do in the mw strategy when we receive an interest */ +} + +void +hicn_receive_data_mw (index_t dpo_idx, int nh_idx) +{ +} + + +/* packet trace format function */ +static u8 * +hicn_strategy_format_trace_mw (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + hicn_strategy_trace_t *t = va_arg (*args, hicn_strategy_trace_t *); + + s = format (s, "Strategy_mw: pkt: %d, sw_if_index %d, next index %d", + (int) t->pkt_type, t->sw_if_index, t->next_index); + return (s); +} + +/* + * Node registration for the forwarder node + */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (hicn_mw_strategy_node) = +{ + .name = "hicn-mw-strategy", + .function = hicn_mw_strategy_node_fn, + .vector_size = sizeof (u32), + .runtime_data_bytes = sizeof (int) + sizeof(hicn_pit_cs_t *), + .format_trace = hicn_strategy_format_trace_mw, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (hicn_strategy_error_strings), + .error_strings = hicn_strategy_error_strings, + .n_next_nodes = HICN_STRATEGY_N_NEXT, + .next_nodes = { + [HICN_STRATEGY_NEXT_INTEREST_HITPIT] = "hicn-interest-hitpit", + [HICN_STRATEGY_NEXT_ERROR_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/strategies/strategy_mw.h b/hicn-plugin/src/strategies/strategy_mw.h new file mode 100755 index 000000000..10b08c05f --- /dev/null +++ b/hicn-plugin/src/strategies/strategy_mw.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_STRATEGY_MW_H__ +#define __HICN_STRATEGY_MW_H__ + +#include "../strategy.h" + +hicn_strategy_vft_t *hicn_mw_strategy_get_vft (void); + +#endif // __HICN_STRATEGY_MW_H__ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/strategies/strategy_mw_cli.c b/hicn-plugin/src/strategies/strategy_mw_cli.c new file mode 100755 index 000000000..ff4125258 --- /dev/null +++ b/hicn-plugin/src/strategies/strategy_mw_cli.c @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include "../strategy_dpo_manager.h" +#include "../faces/face.h" +#include "../error.h" +#include "../route.h" +#include "dpo_mw.h" + +static clib_error_t * +hicn_mw_strategy_cli_set_weight_command_fn (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + clib_error_t *cl_err = 0; + int ret = HICN_ERROR_NONE; + ip46_address_t prefix; + hicn_face_id_t faceid = HICN_FACE_NULL; + u32 fib_index; + u32 weight = HICN_PARAM_FIB_ENTRY_NHOP_WGHT_DFLT; + u32 plen = 0; + hicn_dpo_ctx_t *hicn_dpo_ctx; + const dpo_id_t *hicn_dpo_id; + u32 vft_id; + const hicn_dpo_vft_t *dpo_vft; + + /* Get a line of input. */ + unformat_input_t _line_input, *line_input = &_line_input; + if (unformat_user (main_input, unformat_line_input, line_input)) + { + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "prefix %U/%u", unformat_ip46_address, + &prefix, IP46_TYPE_ANY, &plen)) + ; + else if (unformat (line_input, "face %u", &faceid)) + ; + else if (unformat (line_input, "weight %u", &weight)) + ; + else + { + return clib_error_return (0, "%s", + get_error_string + (HICN_ERROR_CLI_INVAL)); + } + + } + } + + if (((weight < 0) || (weight > HICN_PARAM_FIB_ENTRY_NHOP_WGHT_MAX))) + { + cl_err = clib_error_return (0, + "Next-hop weight must be between 0 and %d", + (int) HICN_PARAM_FIB_ENTRY_NHOP_WGHT_MAX); + goto done; + } + + if (((ip46_address_is_zero (&prefix)) || faceid == HICN_FACE_NULL)) + { + cl_err = + clib_error_return (0, "Please specify prefix and a valid faceid..."); + goto done; + } + + fib_prefix_t fib_pfx; + fib_prefix_from_ip46_addr (&prefix, &fib_pfx); + fib_pfx.fp_len = plen; + + ret = hicn_route_get_dpo (&prefix, plen, &hicn_dpo_id, &fib_index); + + if (ret == HICN_ERROR_NONE) + { + vft_id = hicn_dpo_get_vft_id (hicn_dpo_id); + dpo_vft = hicn_dpo_get_vft (vft_id); + hicn_dpo_ctx = dpo_vft->hicn_dpo_get_ctx (hicn_dpo_id->dpoi_index); + + if (hicn_dpo_ctx == NULL + || hicn_dpo_id->dpoi_type != hicn_dpo_strategy_mw_get_type ()) + { + cl_err = clib_error_return (0, get_error_string (ret)); + goto done; + } + + hicn_strategy_mw_ctx_t *mw_dpo = + (hicn_strategy_mw_ctx_t *) hicn_dpo_ctx; + int idx = ~0; + for (int i = 0; i < hicn_dpo_ctx->entry_count; i++) + if (hicn_dpo_ctx->next_hops[i].dpoi_index == (index_t) faceid) + idx = i; + + if (idx == ~0) + { + cl_err = + clib_error_return (0, + get_error_string + (HICN_ERROR_MW_STRATEGY_NH_NOT_FOUND)); + goto done; + } + + mw_dpo->weight[idx] = weight; + } + else + { + cl_err = clib_error_return (0, get_error_string (ret)); + + } + +done: + + return (cl_err); + +} + +/* cli declaration for 'strategy mw' */ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND(hicn_mw_strategy_cli_set_weight_command, static)= +{ + .path = "hicn strategy mw set", + .short_help = "hicn strategy mw set prefix face weight ", + .function = hicn_mw_strategy_cli_set_weight_command_fn, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/hicn-plugin/src/strategy.c b/hicn-plugin/src/strategy.c new file mode 100755 index 000000000..56de34e6b --- /dev/null +++ b/hicn-plugin/src/strategy.c @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "hicn.h" +#include "parser.h" +#include "strategy.h" +#include "strategy_dpo_ctx.h" +#include "face_db.h" +#include "infra.h" +#include "mgmt.h" +#include "pcs.h" +#include "state.h" + +/* + * Node context data (to be used in all the strategy nodes); we think this is + * per-thread/instance + */ +typedef struct hicn_strategy_runtime_s +{ + int id; + hicn_pit_cs_t *pitcs; +} hicn_strategy_runtime_t; + +always_inline int +hicn_new_interest (hicn_strategy_runtime_t * rt, vlib_buffer_t * b0, + u32 * next, f64 tnow, u8 * nameptr, + u16 namelen, dpo_id_t * outface, int nh_idx, + index_t hicn_dpo_idx, hicn_strategy_vft_t * strategy, + u8 isv6, vl_api_hicn_api_node_stats_get_reply_t * stats) +{ + int ret; + hicn_hash_node_t *nodep; + hicn_pcs_entry_t *pitp; + hicn_header_t *hicn0; + hicn_main_t *sm = &hicn_main; + hicn_buffer_t *hicnb0 = hicn_get_buffer (b0); + u32 node_id0 = 0; + u8 dpo_ctx_id0 = 0; + u8 vft_id0 = 0; + u8 is_cs0 = 0; + u8 hash_entry_id = 0; + u8 bucket_is_overflow = 0; + u32 bucket_id = ~0; + + + /* Create PIT node and init PIT entry */ + nodep = hicn_hashtb_alloc_node (rt->pitcs->pcs_table); + if (PREDICT_FALSE (nodep == NULL)) + { + /* Nothing we can do - no mem */ + *next = HICN_STRATEGY_NEXT_ERROR_DROP; + return HICN_ERROR_HASHTB_NOMEM; + } + pitp = hicn_pit_get_data (nodep); + hicn_pit_init_data (pitp); + pitp->shared.create_time = tnow; + + hicn0 = vlib_buffer_get_current (b0); + hicn_lifetime_t imsg_lifetime; + hicn_type_t type = hicnb0->type; + hicn_ops_vft[type.l1]->get_lifetime (type, &hicn0->protocol, + &imsg_lifetime); + + if (imsg_lifetime < sm->pit_lifetime_min_ms + || imsg_lifetime > sm->pit_lifetime_max_ms) + { + imsg_lifetime = sm->pit_lifetime_dflt_ms; + } + pitp->shared.expire_time = hicn_pcs_get_exp_time (tnow, imsg_lifetime); + + /* Set up the hash node and insert it */ + hicn_hash_entry_t *hash_entry; + hicn_hashtb_init_node (rt->pitcs->pcs_table, nodep, nameptr, namelen); + + ret = + hicn_pcs_pit_insert (rt->pitcs, pitp, nodep, &hash_entry, + hicnb0->name_hash, &node_id0, &dpo_ctx_id0, &vft_id0, + &is_cs0, &hash_entry_id, &bucket_id, + &bucket_is_overflow); + if (ret == HICN_ERROR_NONE) + { + strategy->hicn_add_interest (vnet_buffer (b0)->ip.adj_index[VLIB_TX], + hash_entry); + + /* Add face */ + hicn_face_db_add_face_dpo (&hicnb0->face_dpo_id, &(pitp->u.pit.faces)); + + /* Remove lock on the dpo stored in the vlib_buffer */ + dpo_unlock (&hicnb0->face_dpo_id); + + *next = outface->dpoi_next_node; + + vnet_buffer (b0)->ip.adj_index[VLIB_TX] = outface->dpoi_index; + stats->pkts_interest_count++; + } + else + { + /* Interest aggregate in PIT */ + if (ret == HICN_ERROR_HASHTB_EXIST) + { + hicn_store_internal_state (b0, hicnb0->name_hash, node_id0, + dpo_ctx_id0, vft_id0, hash_entry_id, + bucket_id, bucket_is_overflow); + *next = HICN_STRATEGY_NEXT_INTEREST_HITPIT; + } + else + { + /* Send the packet to the interest-hitpit node */ + *next = HICN_STRATEGY_NEXT_ERROR_DROP; + } + hicn_faces_flush (&(pitp->u.pit.faces)); + hicn_hashtb_free_node (rt->pitcs->pcs_table, nodep); + } + + return (ret); + +} + +/* + * ICN strategy later node for interests: - 1 packet at a time - ipv4/tcp + * ipv6/tcp + */ +uword +hicn_forward_interest_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, + hicn_strategy_vft_t * strategy, + vlib_node_registration_t * hicn_strategy_node) +{ + + u32 n_left_from, *from, *to_next, n_left_to_next; + hicn_strategy_next_t next_index; + hicn_strategy_runtime_t *rt; + vl_api_hicn_api_node_stats_get_reply_t stats = { 0 }; + f64 tnow; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = (hicn_strategy_next_t) node->cached_next_index; + rt = vlib_node_get_runtime_data (vm, hicn_strategy_node->index); + rt->pitcs = &hicn_main.pitcs; + /* Capture time in vpp terms */ + tnow = vlib_time_now (vm); + + while (n_left_from > 0) + { + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + while (n_left_from > 0 && n_left_to_next > 0) + { + u8 isv6; + u8 *nameptr; + u16 namelen; + hicn_name_t name; + hicn_header_t *hicn0; + vlib_buffer_t *b0; + u32 bi0; + dpo_id_t *outface = NULL; + int nh_idx; + u32 next0 = next_index; + int ret; + + /* Prefetch for next iteration. */ + if (n_left_from > 1) + { + vlib_buffer_t *b1; + b1 = vlib_get_buffer (vm, from[1]); + CLIB_PREFETCH (b1, CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH (&b1->trace_index, 2 * CLIB_CACHE_LINE_BYTES, + STORE); + } + /* Dequeue a packet buffer */ + bi0 = from[0]; + from += 1; + n_left_from -= 1; + to_next[0] = bi0; + to_next += 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + next0 = HICN_STRATEGY_NEXT_ERROR_DROP; + + ret = hicn_interest_parse_pkt (b0, &name, &namelen, &hicn0, &isv6); + + stats.pkts_processed++; + /* Select next hop */ + /* + * Double check that the interest has been through + * the interest-pcslookup node due to misconfiguration in + * the punting rules. + */ + if (PREDICT_TRUE + (ret == HICN_ERROR_NONE && HICN_IS_NAMEHASH_CACHED (b0) + && strategy->hicn_select_next_hop (vnet_buffer (b0)-> + ip.adj_index[VLIB_TX], + &nh_idx, + &outface) == + HICN_ERROR_NONE)) + { + /* + * No need to check if parsing was successful + * here. Already checked in the interest_pcslookup + * node + */ + nameptr = (u8 *) (&name); + hicn_new_interest (rt, b0, &next0, tnow, nameptr, namelen, + outface, nh_idx, + vnet_buffer (b0)->ip.adj_index[VLIB_TX], + strategy, isv6, &stats); + } + /* Maybe trace */ + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + hicn_strategy_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->pkt_type = HICN_PKT_TYPE_CONTENT; + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + t->next_index = next0; + } + /* + * Verify speculative enqueue, maybe switch current + * next frame + */ + /* + * Fix in case of a wrong speculation. Needed for + * cloning the data in the right frame + */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, hicn_strategy_node->index, + HICNFWD_ERROR_PROCESSED, stats.pkts_processed); + vlib_node_increment_counter (vm, hicn_strategy_node->index, + HICNFWD_ERROR_INTERESTS, + stats.pkts_interest_count); + + return (frame->n_vectors); +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/strategy.h b/hicn-plugin/src/strategy.h new file mode 100755 index 000000000..6b06a6ce9 --- /dev/null +++ b/hicn-plugin/src/strategy.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_STRATEGY__ +#define __HICN_STRATEGY__ + +#include "hicn.h" +#include "hashtb.h" +#include "mgmt.h" +#include "faces/face.h" + +/** + * @File + * + * A strategy is defined as a vpp node and a set of function that will be called + * during the packet processing. Having one vpp node per strategy allows to + * easily process multiple interests in the same node (x2 or x4) and call the + * same function for choosing the next hop. + * Here we provide: + * - a template for the callbacks to implement in order to create a new strategy + * (hicn_fwd_strategy_t) + * - the base structure for a strategy node + * (list of next vpp nodes, errors, tracing and the main function processing an + * interest and calling hicn_select_next_hop) + */ + +typedef struct hicn_strategy_vft_s +{ + void (*hicn_receive_data) (index_t dpo_idx, int nh_idx); + void (*hicn_on_interest_timeout) (index_t dpo_idx); + void (*hicn_add_interest) (index_t dpo_idx, hicn_hash_entry_t * pit_entry); + u32 (*hicn_select_next_hop) (index_t dpo_idx, int *nh_idx, + dpo_id_t ** outface); + u32 (*get_strategy_node_index) (void); + /**< Return the vlib node index implementing the strategy */ +} hicn_strategy_vft_t; + +hicn_face_vft_t *hicn_strategy_get_face_vft (u16 index); + +/* Strategy node API */ +/* Basic interest processing function. To be called in all the strategy nodes */ +uword +hicn_forward_interest_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, + hicn_strategy_vft_t * strategy, + vlib_node_registration_t * hicn_strategy_node); + +/* Trace context struct */ +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 pkt_type; +} hicn_strategy_trace_t; + +typedef enum +{ + HICN_STRATEGY_NEXT_INTEREST_HITPIT, + HICN_STRATEGY_NEXT_ERROR_DROP, + HICN_STRATEGY_N_NEXT, +} hicn_strategy_next_t; + +#endif /* //__HICN_STRATEGY__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/strategy_dpo_ctx.h b/hicn-plugin/src/strategy_dpo_ctx.h new file mode 100755 index 000000000..5d2dbc47c --- /dev/null +++ b/hicn-plugin/src/strategy_dpo_ctx.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_STRATEGY_DPO_CTX_H__ +#define __HICN_STRATEGY_DPO_CTX_H__ + +#include +#include + +#include "hicn.h" +#include "params.h" +#include "faces/face.h" + +#define HICN_FIB_TABLE 0 + +#define DATA_LEN 8 + +#define NEXT_HOP_INVALID DPO_INVALID + +/* + * An hicn dpo is a list of next hops (face + weight). + */ +typedef struct __attribute__ ((packed)) hicn_dpo_ctx_s +{ + CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); + /* 8B*5 = 40B */ + dpo_id_t next_hops[HICN_PARAM_FIB_ENTRY_NHOPS_MAX]; + /* 40B + 4B = 44B */ + u32 locks; + /* 44B + 1B = 45B */ + u8 entry_count; + /* 45B + 1B = 46B */ + /* Number of TFIB entries (stored at the end of the next_hops array */ + u8 tfib_entry_count; + + /* 46B + 2B = 48B */ + u16 padding; /* To align to 8B */ + +#ifdef HICN_MAPME_NOTIFICATIONS + /* (8B) last acked update for IU/IN heuristic on producer */ + f64 last_iu_ack; +#endif + /* (4B) last sequence number */ + seq_t seq; + +} hicn_dpo_ctx_t; + +STATIC_ASSERT (sizeof (hicn_dpo_ctx_t) <= CLIB_CACHE_LINE_BYTES, + "sizeof hicn_dpo_ctx_t is greater than 64B"); + +#endif /* // __HICN_STRATEGY_DPO_CTX_H__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/strategy_dpo_manager.c b/hicn-plugin/src/strategy_dpo_manager.c new file mode 100755 index 000000000..c1723eccc --- /dev/null +++ b/hicn-plugin/src/strategy_dpo_manager.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "strategy_dpo_manager.h" +#include "strategies/dpo_mw.h" +#include "strategy.h" +#include "faces/face.h" + +static dpo_type_t *strategies_id; +static const hicn_dpo_vft_t **hicn_dpo_vfts; + +static const hicn_strategy_vft_t **hicn_strategy_vfts; + +int hicn_strategies = 0; + +hicn_dpo_vft_t default_dpo; + +dpo_type_t +hicn_dpo_register_new_type (const char *const *const *hicn_nodes, + const hicn_dpo_vft_t * hicn_dpo_vft, + const hicn_strategy_vft_t * hicn_strategy_vft, + const dpo_vft_t * dpo_ctx_vft) +{ + dpo_type_t dpo_type = dpo_register_new_type (dpo_ctx_vft, hicn_nodes); + vec_validate (hicn_dpo_vfts, dpo_type); + hicn_dpo_vfts[dpo_type] = hicn_dpo_vft; + + vec_validate (hicn_strategy_vfts, dpo_type); + hicn_strategy_vfts[dpo_type] = hicn_strategy_vft; + + vec_validate (strategies_id, hicn_strategies); + strategies_id[hicn_strategies] = dpo_type; + hicn_strategies++; + + return dpo_type; +} + +u32 +dpo_is_hicn (const dpo_id_t * dpo) +{ + for (int i = 0; i < hicn_strategies; i++) + { + if (hicn_dpo_vfts[strategies_id[i]]->hicn_dpo_is_type (dpo)) + return 1; + } + return 0; +} + +dpo_type_t +hicn_dpo_get_vft_id (const dpo_id_t * dpo) +{ + return dpo->dpoi_type; +} + +const hicn_dpo_vft_t * +hicn_dpo_get_vft (dpo_type_t vfts_id) +{ + return hicn_dpo_vfts[vfts_id]; +} + +const hicn_dpo_vft_t * +hicn_dpo_get_vft_from_id (u8 strategy_id) +{ + return hicn_dpo_vfts[strategies_id[strategy_id]]; +} + +const hicn_strategy_vft_t * +hicn_dpo_get_strategy_vft (dpo_type_t vfts_id) +{ + return hicn_strategy_vfts[vfts_id]; +} + +const hicn_strategy_vft_t * +hicn_dpo_get_strategy_vft_from_id (u8 vfts_id) +{ + return hicn_strategy_vfts[strategies_id[vfts_id]]; +} + +void +hicn_dpos_init (void) +{ + hicn_dpo_strategy_mw_module_init (); + + default_dpo.hicn_dpo_get_ctx = &hicn_strategy_mw_ctx_get; + default_dpo.hicn_dpo_is_type = &hicn_dpo_is_type_strategy_mw; + default_dpo.hicn_dpo_get_type = &hicn_dpo_strategy_mw_get_type; + default_dpo.hicn_dpo_module_init = &hicn_dpo_strategy_mw_module_init; + default_dpo.hicn_dpo_create = &hicn_strategy_mw_ctx_create; + default_dpo.hicn_dpo_add_update_nh = &hicn_strategy_mw_ctx_add_nh; + default_dpo.hicn_dpo_del_nh = &hicn_strategy_mw_ctx_del_nh; + default_dpo.hicn_dpo_lock_dpo_ctx = &hicn_strategy_mw_ctx_lock; + default_dpo.hicn_dpo_unlock_dpo_ctx = hicn_strategy_mw_ctx_unlock; + default_dpo.format_hicn_dpo = &format_hicn_strategy_mw_ctx; +} + +u8 * +format_hicn_strategy_list (u8 * s, int n, ...) +{ + va_list ap; + va_start (ap, n); + u32 indent = va_arg (ap, u32); + va_end (ap); + + s = format (s, "Strategies:\n", indent); + indent += 4; + int i; + vec_foreach_index (i, strategies_id) + { + s = format (s, "(%d) ", i, indent); + s = hicn_dpo_vfts[strategies_id[i]]->format_hicn_dpo (s, &ap); + } + + return (s); +} + +u8 +hicn_dpo_strategy_id_is_valid (int strategy_id) +{ + return vec_len (strategies_id) > strategy_id ? + HICN_ERROR_NONE : HICN_ERROR_DPO_MGR_ID_NOT_VALID; +} + +int +hicn_strategy_get_all_available (void) +{ + return hicn_strategies; +} + +/** + * @brief Registers a dpo by calling its module init function. + * + * This is typically called from the ctor for dpo's registered at compilation + * time. + */ +void +hicn_dpo_register (const hicn_dpo_vft_t * hicn_dpo) +{ + hicn_dpo->hicn_dpo_module_init (); +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/strategy_dpo_manager.h b/hicn-plugin/src/strategy_dpo_manager.h new file mode 100755 index 000000000..686c2f8c8 --- /dev/null +++ b/hicn-plugin/src/strategy_dpo_manager.h @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_STRATEGY_DPO_MANAGER_H__ +#define __HICN_STRATEGY_DPO_MANAGER_H__ + +#include "strategy_dpo_ctx.h" +#include "strategy.h" + +/** + * @brief Definition of the virtual function table for a hICN DPO. + * + * An hICN dpo is a combination of a dpo context (hicn_dpo_ctx or struct that + * extends a hicn_dpo_ctx) and a strategy node. The following virtual function table + * template that glues together the fuction to interact with the context and the + * creating the dpo + */ +typedef struct hicn_dpo_vft_s +{ + hicn_dpo_ctx_t *(*hicn_dpo_get_ctx) (index_t dpo_idx); /**< Retrieve the dpo ctx*/ + int (*hicn_dpo_is_type) (const dpo_id_t * dpo); + /**< Check if the type of the + hICN DPO is the expected */ + dpo_type_t (*hicn_dpo_get_type) (void); + /**< Return the type of the hICN dpo */ + void (*hicn_dpo_module_init) (void); /**< Initialize the hICN dpo */ + int (*hicn_dpo_create) (dpo_proto_t proto, const dpo_id_t * nh, int nh_len, index_t * dpo_idx); /**< Create the context of the hICN dpo */ + int (*hicn_dpo_add_update_nh) (const dpo_id_t * nh, index_t dpo_idx); /**< Add a next hop to the hICN dpo context */ + int (*hicn_dpo_del_nh) (hicn_face_id_t face_id, index_t dpo_idx, + fib_prefix_t * fib_pfx); + /**< Add a next hop to the hICN dpo context */ + void (*hicn_dpo_lock_dpo_ctx) (dpo_id_t * dpo); + void (*hicn_dpo_unlock_dpo_ctx) (dpo_id_t * dpo); + u8 *(*format_hicn_dpo) (u8 * s, va_list * ap); + /**< Format an hICN dpo*/ +} hicn_dpo_vft_t; + +/* + * Default dpo to be used to create fib entry when a strategy is not + * specified + */ +extern hicn_dpo_vft_t default_dpo; + +/** + * @brief Register a new hICN dpo to the manager. + * + * An hICN DPO is a combination of: + * - a hICN DPO ctx (context) that holds the structure containing the + * information to choose the next hop, + * - a strategy containing: (i) the vpp node that processes Interest packets + * subjected to such strategy, (ii) the definition of the vft that defines + * the hICN strategy functions + * Registering a hICN DPO allows the plugin to be aware of the new dpo an be + * able to apply it to the FIB entries. + * + * @param hicn_nodes A list of vpp to which pass an interest that matches with + * the FIB entry to which the hICN DPO is applied. This list must contain the + * name of the strategy node (or nodes in case of differentiation between IPv4 + * and IPv6). + * @param hicn_dpo_vft The structure holding the virtual function table to + * interact with the hICN dpo and its context. + * @param hicn_strategy_vft The structure holding the virtual function table + * containing the hICN strategy functions. + * @return the dpo type registered in the VPP Data plane graph. + */ +dpo_type_t +hicn_dpo_register_new_type (const char *const *const *hicn_nodes, + const hicn_dpo_vft_t * hicn_dpo_vft, + const hicn_strategy_vft_t * + hicn_strategy_vft, const dpo_vft_t * dpo_ctx_vft); + +/** + * @brief Check if the type of the dpo is among the list of hicn dpo types + * + * Iterate through the list of dpo types registered in the hicn dpo manager. + * + * @param dpo The id of the dpo to which check the type + * @return 1 if there is a match, 0 otherwise. + */ +u32 dpo_is_hicn (const dpo_id_t * dpo); + +/** + * @brief Return the dpo_vtf and strategy_vtf identifier + * + * Iterate through the list of dpo types registered in the hicn dpo manager and + * retrieve the corresponding dpo_vtf/strategy_vtf identifier. + * + * @param dpo The id of the dpo to which check the type + * @return the dpo_vft/strategy_vft id or HICN_ERROR_DPO_NOT_FOUND in case the dpo is not an hICN dpo. + */ +u8 hicn_dpo_get_vft_id (const dpo_id_t * dpo); + +/** + * @brief Get the vft to manage the dpo context. + * + * @param The id of the hicn_dpo_vft to retrieve. + * @return The vft struct that contains the list of callbacks that allows to + * manage the dpo context. + */ +const hicn_dpo_vft_t *hicn_dpo_get_vft (dpo_type_t vfts_id); + +/** + * @brief Get the vft to manage the dpo context from the strategy id. + * + * @param The strategy id of the hicn_dpo_vft to retrieve. + * @return The vft struct that contains the list of callbacks that allows to + * manage the dpo context. + */ +const hicn_dpo_vft_t *hicn_dpo_get_vft_from_id (u8 strategy_id); + +/** + * @brief Get the vft with the hICN strategy functions. + * + * @param The id of the hicn_strategy_vft to retrieve. + * @return The vft struct that contains the list hICN strategy functions. + */ +const hicn_strategy_vft_t *hicn_dpo_get_strategy_vft (dpo_type_t vfts_id); + +/** + * @brief Get the vft with the hICN strategy functions from the strategy id. + * + * @param The id of the hicn_strategy_vft to retrieve. + * @return The vft struct that contains the list hICN strategy functions. + */ +const hicn_strategy_vft_t *hicn_dpo_get_strategy_vft_from_id (u8 vfts_id); + +/** + * @brief Initialize all the types hicn dpo registered + * + * Call the init functions of all the hicn dpo implemented. + * This init is called when the plugin bootstrap. + */ +void hicn_dpos_init (void); + +/** + * @brief Print the list of the registered hICN DPO + * + * @param s String to which to append the list of hICN DPO (strategies) + * @param n number of parameters to pass + * + * @result The string with the list of hICN DPO (strategies) + */ +u8 *format_hicn_strategy_list (u8 * s, int n, ...); + +/** + * @brief Check if a given id points to a strategy and the corresponding dpo ctx + * + * @param The id of the strategy to check. + * + * @result HICN_ERROR_NONE is the id is valid, otherwise EINVAL + */ +u8 hicn_dpo_strategy_id_is_valid (int strategy_id); + +/** + * @brief Return the number of available strategies. This number can be used to + * as an upperbond for valid vfts_id. + * + * @result Return the number of available strategies. + */ +int hicn_strategy_get_all_available (void); + +/** + * @brief Registers a module at compilation time to be initialized as part of + * the ctor. + */ +void hicn_dpo_register (const hicn_dpo_vft_t * hicn_dpo); + +#endif /* // __HICN_STRATEGY_DPO_MANAGER_H__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/utils.h b/hicn-plugin/src/utils.h new file mode 100755 index 000000000..ecad47e9b --- /dev/null +++ b/hicn-plugin/src/utils.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_UTILS_H__ +#define __HICN_UTILS_H__ + +#include "hicn.h" + +always_inline void +hicn_print_name6 (hicn_name_t * name) +{ + u8 *s0; + s0 = format (0, "Source addr %U, seq_number %u", format_ip6_address, + (ip6_address_t *) name->ip6.prefix, + clib_net_to_host_u32 (name->ip6.suffix)); + + printf ("%s\n", s0); +} + +always_inline void +hicn_print6 (hicn_header_t * hicn0) +{ + vlib_main_t *vm = vlib_get_main (); + u8 *s0; + s0 = format (0, "Source addr %U:%u, dest addr %U:%u", format_ip6_address, + &(hicn0->v6.ip.saddr), + clib_net_to_host_u32 (hicn0->v6.tcp.seq), format_ip6_address, + &(hicn0->v6.ip.daddr), + clib_net_to_host_u32 (hicn0->v6.tcp.seq)); + + vlib_cli_output (vm, "%s\n", s0); +} + +always_inline void +hicn_print4 (hicn_header_t * hicn0) +{ + u8 *s0; + s0 = format (0, "Source addr %U:%u, dest addr %U:%u", format_ip4_address, + &(hicn0->v4.ip.saddr), + clib_net_to_host_u32 (hicn0->v4.tcp.seq), format_ip4_address, + &(hicn0->v4.ip.daddr), + clib_net_to_host_u32 (hicn0->v4.tcp.seq)); + + printf ("%s\n", s0); +} + +#endif /* // __HICN_UTILS_H__ */ + + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: eval: (c-set-style "gnu") End: + */ diff --git a/hicn-plugin/src/vface_db.h b/hicn-plugin/src/vface_db.h new file mode 100755 index 000000000..b98a2f46d --- /dev/null +++ b/hicn-plugin/src/vface_db.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HICN_FACE_DB_H__ +#define __HICN_FACE_DB_H__ + +#include +#include "faces/face.h" + +/* Must be power of two*/ +#define HICN_FACE_DB_INLINE_FACES 4 + +#define HICN_PIT_N_HOP_BITMAP_SIZE HICN_PARAM_PIT_ENTRY_PHOPS_MAX + +#define HICN_PIT_N_HOP_BUCKET (HICN_PARAM_PIT_ENTRY_PHOPS_MAX - HICN_FACE_DB_INLINE_FACES) + +STATIC_ASSERT ((HICN_PIT_N_HOP_BUCKET & (HICN_PIT_N_HOP_BUCKET - 1)) == 0, + "HICN_PARAM_PIT_ENTRY_PHOP_MAX must be a power of 2 + 4"); + +/* Takes 2 cache lines */ +typedef struct __attribute__ ((packed)) hicn_face_bucket_s +{ + /* Array of indexes of virtual faces */ + dpo_id_t faces[HICN_PIT_N_HOP_BUCKET]; + + CLIB_CACHE_LINE_ALIGN_MARK (cache_line1); + + /* Used to check if interests are retransmission */ + /* How much are we gaining (performance)/wasting (memory) wrt the linear */ + /* search on the array of faces? */ + u8 bitmap[HICN_PIT_N_HOP_BITMAP_SIZE]; + +} hicn_face_bucket_t; + +extern hicn_face_bucket_t *hicn_face_bucket_pool; + +/* + * Virtual faces will be stored in a pool and when a virtual face is created and + * its index will be saved in the pit entry. In case of interest aggregation we + * have to look on all the virtual faces to understand if there is a duplicated + * interest + */ +typedef struct __attribute__ ((packed)) hicn_face_db_s +{ + /* 19B + 1B = 20B */ + /* Equal to one or zero */ + u8 is_overflow; + + /* Number of faces in the last bucket */ + /* Or next availabe entry for storing a dpo_id_t */ + /* 20B + 4B = 24B */ + u32 n_faces; + + /* 24B + 32B (8*4) = 56B */ + /* Array of indexes of virtual faces */ + dpo_id_t inline_faces[HICN_FACE_DB_INLINE_FACES]; + + /* 56B + 4B = 60B */ + u32 next_bucket; + + /* 60B + 4B = 64B */ + u32 align; //align back to 64 + +} hicn_face_db_t; + +//STATIC_ASSERT(HICN_PIT_N_HOP_BITMAP_SIZE <= (HICN_PARAM_PIT_ENTRY_PHOPS_MAX/8)); + +always_inline dpo_id_t * +hicn_face_db_get_dpo_face (u32 index, hicn_face_db_t * face_db) +{ + ASSERT (index < face_db->n_faces); + + return index < HICN_FACE_DB_INLINE_FACES ? &(face_db->inline_faces[index]) : + &(pool_elt_at_index (hicn_face_bucket_pool, face_db->next_bucket)->faces + [(index - HICN_FACE_DB_INLINE_FACES) & (HICN_PIT_N_HOP_BUCKET - 1)]); +} + +always_inline void +hicn_face_db_init (int max_element) +{ + pool_init_fixed (hicn_face_bucket_pool, max_element); +} + +always_inline hicn_face_bucket_t * +hicn_face_db_get_bucket (u32 bucket_index) +{ + return pool_elt_at_index (hicn_face_bucket_pool, bucket_index); +} + +always_inline void +hicn_face_db_add_face_dpo (dpo_id_t * dpo, hicn_face_db_t * face_db) +{ + ASSERT (dpo->dpoi_index != ~0); + + hicn_face_bucket_t *faces_bkt = + pool_elt_at_index (hicn_face_bucket_pool, face_db->next_bucket); + dpo_id_t *face = + face_db->n_faces < + HICN_FACE_DB_INLINE_FACES ? &(face_db->inline_faces[face_db->n_faces]) : + &(faces_bkt->faces + [(face_db->n_faces - + HICN_FACE_DB_INLINE_FACES) & (HICN_PIT_N_HOP_BUCKET - 1)]); + + clib_memcpy (face, dpo, sizeof (dpo_id_t)); + + /* This access the dpoi to increase the lock */ + dpo_lock (dpo); + + u32 bitmap_index = dpo->dpoi_index % HICN_PIT_N_HOP_BITMAP_SIZE; + faces_bkt->bitmap[bitmap_index] |= 0x01; + face_db->n_faces++; +} + +always_inline u8 +hicn_face_search (dpo_id_t * dpo, hicn_face_db_t * face_db) +{ + hicn_face_bucket_t *faces_bkt = + pool_elt_at_index (hicn_face_bucket_pool, face_db->next_bucket); + u32 bitmap_index = dpo->dpoi_index % HICN_PIT_N_HOP_BITMAP_SIZE; + + return faces_bkt->bitmap[bitmap_index] & 0x01; +} + +always_inline void +hicn_faces_flush (hicn_face_db_t * face_db) +{ + hicn_face_bucket_t *faces_bkt = + pool_elt_at_index (hicn_face_bucket_pool, face_db->next_bucket); + clib_memset_u64 (&(faces_bkt->bitmap), 0, HICN_PIT_N_HOP_BITMAP_SIZE / 8); + face_db->n_faces = 0; + pool_put_index (hicn_face_bucket_pool, face_db->next_bucket); +} + + +#endif // __HICN_FACE_DB_H__ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt new file mode 100755 index 000000000..1be5e67f9 --- /dev/null +++ b/lib/CMakeLists.txt @@ -0,0 +1,50 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required (VERSION 3.5 FATAL_ERROR) +project(Hicn C) +include(CTest) + +set(CMAKE_MODULE_PATH + ${CMAKE_MODULE_PATH} + "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/Modules" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules" +) + +if (NOT CMAKE_BUILD_TYPE) + message(STATUS "No build type selected, default to Release") + set(CMAKE_BUILD_TYPE "Release") +endif() + +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + set(LIBHICN hicn) +endif() + +set(CMAKE_VERSION_MAJOR 0) +set(CMAKE_VERSION_MINOR 1) +set(CMAKE_VERSION_PATCH 1) + +option(CMAKE_BUILD_TEST "Build unit tests" OFF) + +if (NOT CMAKE_BUILD_TYPE) + message(STATUS "No build type selected, default to Release") + set(CMAKE_BUILD_TYPE "Release") +endif() + +set(CMAKE_C_FLAGS -Wall) + +if (ANDROID_API) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ANDROID_C_FLAGS} -std=c99") +endif () + +add_subdirectory (src) diff --git a/lib/README.md b/lib/README.md new file mode 100755 index 000000000..6dac988db --- /dev/null +++ b/lib/README.md @@ -0,0 +1,114 @@ +# libhicn + +## Introduction + +libhicn provides a support library coded in C designed to help developers embed +Hybrid ICN (hICN) functionalities in their applications (eg. forwarder, socket +API, etc.). Its purpose is to follow the hICN specification for which it +provides a reference implementation, abstracting the user from all internal +mechanisms, and offering an API independent of the packet format (eg. IPv4 or +IPv6). The library is designed to be portable across both desktop and +mobile platforms, and we currently aim at supporting Linux, Android, OSX and +iOS, by writing the necessary adapters to realize hICN functionality in +userspace according to the available APIs and permissions that each system +offers. + +The library consists in several layers: +- the core library (hicn.h) provides a standard hICN packet format, as well as +an API allowing manipulation of packet headers; +- an hICN helper, allowing an hICN stack to be built in userspace in a portable +way, based on TUN devices and accessible though file descriptors; +- a network layer allow the sending an receiving of hICN packets on those file +descriptors, implementing both source and destination address translation as +required by the hICN mechanisms; +- finally, a "transport" API allows the forging of dummy interest and data +packets. + +A commandline interface (hicnc) is also provided that uses the library and can +for instance be used as a test traffic generator. This interface can be run as +either a consumer, a producer, or a simple forwarder. + +## Folder content + +CMakeLists.txt CMkake global build file +doc Package documentation +README.md This file +src + base.h Base definitions for hICN implementation + CMakeLists.txt CMake library build file + common.{h,c} Harmonization layer across supported platforms + compat.{h,c} Compatibility layer for former API + error.{h,c} Error management files + header.h hICN header definitions + hicn.h Master include file + mapme.{h,c} MAP-Me : anchorless producer mobility mechanisms + name.{h,c} hICN naming conventions and name processing + IP helpers + ops.{h,c} Protocol-independent hICN operations + protocol/* Protocol headers + protocol-dependent implementations + protocol.h Common file for protocols + +## Using libhicn + +### Platforms ### + +libhicn has been tested in: + +- Ubuntu 16.04 LTS (x86_64) +- Ubuntu 18.04 LTS (x86_64) +- Debian Stable/Testing +- Red Hat Enterprise Linux 7 +- CentOS 7 +- Android 8 +- iOS 12 +- macOS 10.12 +- Windows 10 + +Other platforms and architectures may work. + +### Dependencies + +Build dependencies: + +- c11 ( clang / gcc ) +- CMake 3.4 + +Basic dependencies: None + +## Installation + +You can either use released packages, or compile libhicn from sources. + +### Release mode + +mkdir build +cd build +cmake .. +make +sudo make install + +### Debug mode + +mkdir debug +cd debug +cmake .. -DCMAKE_BUILD_TYPE=Debug +make +sudo make install + +## License + +This software is distributed under the following license: + +``` +Copyright (c) 2017-2019 Cisco and/or its affiliates. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` diff --git a/lib/doc/CMakeLists.txt b/lib/doc/CMakeLists.txt new file mode 100755 index 000000000..cf022dc52 --- /dev/null +++ b/lib/doc/CMakeLists.txt @@ -0,0 +1,23 @@ +# add a target to generate API documentation with Doxygen +find_package(Doxygen) +option(BUILD_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" ${DOXYGEN_FOUND}) + +if(BUILD_DOCUMENTATION) + if(NOT DOXYGEN_FOUND) + message(FATAL_ERROR "Doxygen is needed to build the documentation.") + endif() + + set(doxyfile_in ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in) + set(doxyfile ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) + + configure_file(${doxyfile_in} ${doxyfile} @ONLY) + + add_custom_target(doc + COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating API documentation with Doxygen" + VERBATIM) + +# FIXME MS : wrong install path + install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION share/doc) +endif() diff --git a/lib/doc/Doxyfile.in b/lib/doc/Doxyfile.in new file mode 100755 index 000000000..839de9f8a --- /dev/null +++ b/lib/doc/Doxyfile.in @@ -0,0 +1,12 @@ +PROJECT_NAME = "Hybrid ICN (hICN)" +PROJECT_NUMBER = v@CMAKE_VERSION_MAJOR@.@CMAKE_VERSION_MINOR@.@CMAKE_VERSION_PATCH@ +STRIP_FROM_PATH = @PROJECT_SOURCE_DIR@ \ + @PROJECT_BINARY_DIR@ +INPUT = @doxy_main_page@ \ + @PROJECT_SOURCE_DIR@ \ + @PROJECT_BINARY_DIR@ +FILE_PATTERNS = *.md \ + *.h \ + *.cc +RECURSIVE = YES +USE_MDFILE_AS_MAINPAGE = ../README.md diff --git a/lib/src/CMakeLists.txt b/lib/src/CMakeLists.txt new file mode 100755 index 000000000..0137a16c7 --- /dev/null +++ b/lib/src/CMakeLists.txt @@ -0,0 +1,81 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND LIBHICN_HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/hicn.h + ${CMAKE_CURRENT_SOURCE_DIR}/base.h + ${CMAKE_CURRENT_SOURCE_DIR}/common.h + ${CMAKE_CURRENT_SOURCE_DIR}/compat.h + ${CMAKE_CURRENT_SOURCE_DIR}/error.h + ${CMAKE_CURRENT_SOURCE_DIR}/header.h + ${CMAKE_CURRENT_SOURCE_DIR}/mapme.h + ${CMAKE_CURRENT_SOURCE_DIR}/name.h + ${CMAKE_CURRENT_SOURCE_DIR}/protocol.h + ${CMAKE_CURRENT_SOURCE_DIR}/ops.h +) + +list(APPEND LIBHICN_HEADER_FILES_PROTOCOL + ${CMAKE_CURRENT_SOURCE_DIR}/protocol/ah.h + ${CMAKE_CURRENT_SOURCE_DIR}/protocol/icmp.h + ${CMAKE_CURRENT_SOURCE_DIR}/protocol/icmprd.h + ${CMAKE_CURRENT_SOURCE_DIR}/protocol/ipv4.h + ${CMAKE_CURRENT_SOURCE_DIR}/protocol/ipv6.h + ${CMAKE_CURRENT_SOURCE_DIR}/protocol/tcp.h + ${CMAKE_CURRENT_SOURCE_DIR}/protocol/udp.h +) + +list(APPEND LIBHICN_SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/compat.c + ${CMAKE_CURRENT_SOURCE_DIR}/error.c + ${CMAKE_CURRENT_SOURCE_DIR}/mapme.c + ${CMAKE_CURRENT_SOURCE_DIR}/name.c + ${CMAKE_CURRENT_SOURCE_DIR}/ops.c + ${CMAKE_CURRENT_SOURCE_DIR}/common.c + ${CMAKE_CURRENT_SOURCE_DIR}/protocol/ah.c + ${CMAKE_CURRENT_SOURCE_DIR}/protocol/icmp.c + ${CMAKE_CURRENT_SOURCE_DIR}/protocol/ipv4.c + ${CMAKE_CURRENT_SOURCE_DIR}/protocol/ipv6.c + ${CMAKE_CURRENT_SOURCE_DIR}/protocol/tcp.c +) + +set (COMPILER_DEFINITIONS "-DWITH_MAPME -DWITH_MAPME_FIXES") + +include(BuildMacros) +build_library(${LIBHICN} + SHARED STATIC + SOURCES ${LIBHICN_SOURCE_FILES} + COMPONENT libhicn + INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/.. + DEFINITIONS ${COMPILER_DEFINITIONS} + INSTALL_ROOT_DIR hicn + INSTALL_HEADERS ${LIBHICN_HEADER_FILES} ${LIBHICN_HEADER_FILES_PROTOCOL} +) + +add_custom_command(TARGET hicn PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E remove_directory ${PROJECT_BINARY_DIR}/hicn +) + +add_custom_command(TARGET hicn POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/hicn/ + COMMAND ${CMAKE_COMMAND} -E copy ${LIBHICN_HEADER_FILES} ${PROJECT_BINARY_DIR}/hicn/ +) + +add_custom_command(TARGET hicn POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/hicn/protocol + COMMAND ${CMAKE_COMMAND} -E copy ${LIBHICN_HEADER_FILES_PROTOCOL} ${PROJECT_BINARY_DIR}/hicn/protocol +) + +# install(FILES ${LIBHICN_HEADER_FILES} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/hicn COMPONENT libhicn) +# install(FILES ${LIBHICN_HEADER_FILES_PROTOCOL} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/hicn/protocol COMPONENT libhicn) diff --git a/lib/src/base.h b/lib/src/base.h new file mode 100755 index 000000000..c1bd23aeb --- /dev/null +++ b/lib/src/base.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file base.h + * @brief Base hICN definitions. + */ + +#ifndef HICN_BASE_H +#define HICN_BASE_H + +#include "common.h" + +/* Default header fields */ +#define HICN_DEFAULT_TTL 254 + +typedef u32 hicn_faceid_t; +typedef u8 hicn_pathlabel_t; +typedef u32 hicn_lifetime_t; + +#define HICN_MAX_LIFETIME HICN_MAX_LIFETIME_SCALED << HICN_MAX_LIFETIME_MULTIPLIER +#define HICN_MAX_LIFETIME_SCALED 0xFFFF +#define HICN_MAX_LIFETIME_MULTIPLIER 0xF /* 4 bits */ + +/** + * @brief hICN packet format type + * + * The hICN type represents the sequence of protocols that we can find in packet + * headers. They are represented as a quartet of u8 values, correponding to + * IANA protocol assignment, and read from right to left. This is done to + * faciliate decapsulation of packet header by simple shift/mask operations. + * + * For instance, an IPv6/TCP packet will be identified as : + * [IPPROTO_NONE, IPPROTO_NONE, IPPROTO_TCP, IPPROTO_IPV6] + * + * We expect four elements to be sufficient for most uses, the max being + * currently used by an hypothetical signed MAP-Me update : + * [IPPROTO_ICMPRD, IPPROTO_AH, IPPROTO_ICMP, IPPROTO_IPV6] + */ +typedef union +{ + /** protocol layers representation */ + struct + { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + u8 l1; /**< First layer */ + u8 l2; /**< Second layer */ + u8 l3; /**< Third layer */ + u8 l4; /**< Fourth layer */ +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + u8 l4; /**< Fourth layer */ + u8 l3; /**< Third layer */ + u8 l2; /**< Second layer */ + u8 l1; /**< First layer */ +#else +#error "Unsupported endianness" +#endif + }; + /** u32 representation */ + u32 as_u32; +} hicn_type_t; + +/* Common protocol layers */ +#define HICN_TYPE_IPV4_TCP (hicn_type_t) {{ .l4 = IPPROTO_NONE, .l3 = IPPROTO_NONE, .l2 = IPPROTO_TCP, .l1 = IPPROTO_IP }} +#define HICN_TYPE_IPV4_ICMP (hicn_type_t) {{ .l4 = IPPROTO_NONE, .l3 = IPPROTO_NONE, .l2 = IPPROTO_ICMP, .l1 = IPPROTO_IP }} +#define HICN_TYPE_IPV6_TCP (hicn_type_t) {{ .l4 = IPPROTO_NONE, .l3 = IPPROTO_NONE, .l2 = IPPROTO_TCP, .l1 = IPPROTO_IPV6 }} +#define HICN_TYPE_IPV6_ICMP (hicn_type_t) {{ .l4 = IPPROTO_NONE, .l3 = IPPROTO_NONE, .l2 = IPPROTO_ICMPV6, .l1 = IPPROTO_IPV6 }} + + +/** + * @brief hICN Payload type + * + * This type distinguishes several types of data packet, which can either carry + * content data, or Manifest + */ +typedef enum +{ + HPT_DATA = 0, + HPT_MANIFEST = 1, + HPT_UNSPEC = 999 +} hicn_payload_type_t; + +/** + * @brief Path label computations + * + * Path label is computed by accumulating the identifiers of successive output + * faces as a Data packet is traveling from its producer back to the consumer + * originating the Interest. + * + * NOTE: this computation is not (yet) part of the hICN specification. + */ + +#define HICN_PATH_LABEL_MASK 0xF000 /* 1000 0000 0000 0000 */ +#define HICN_PATH_LABEL_SIZE 8 + +/** + * @brief Path label update + * @param [in] current_label Current pathlabel + * @param [in] face_id The face identifier to combine into the path label + * @param [out] new_label Computed pathlabel + * + * This function updates the current_label based on the new face_id, and returns + */ +always_inline void +update_pathlabel (hicn_pathlabel_t current_label, hicn_faceid_t face_id, + hicn_pathlabel_t * new_label) +{ + hicn_pathlabel_t pl_face_id = + (hicn_pathlabel_t) ((face_id & HICN_PATH_LABEL_MASK) >> + (16 - HICN_PATH_LABEL_SIZE)); + *new_label = + ((current_label << 1) | (current_label >> (HICN_PATH_LABEL_SIZE - 1))) ^ + pl_face_id; +} + +#endif /* HICN_BASE_H */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/common.c b/lib/src/common.c new file mode 100755 index 000000000..4b7c4a2a7 --- /dev/null +++ b/lib/src/common.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file common.c + * @brief Implementation of common interfaces abstracting low-level platform. + * details. + */ + +#include +#include // memset +#include // getaddrinfo +#include // '' +#include // '' +#include + +#include "common.h" + + +int +get_addr_family (const char *ip_address) +{ + struct addrinfo hint, *res = NULL; + int rc; + + memset (&hint, '\0', sizeof hint); + + hint.ai_family = PF_UNSPEC; + hint.ai_flags = AI_NUMERICHOST; + + rc = getaddrinfo (ip_address, NULL, &hint, &res); + if (rc) + { + return -1; + } + rc = res->ai_family; + freeaddrinfo (res); + return rc; +} + +/* hashes */ + +u32 +cumulative_hash32 (const void *data, size_t len, u32 lastValue) +{ + // Standard FNV 32-bit prime: see http://www.isthe.com/chongo/tech/comp/fnv/#FNV-param + const u32 fnv1a_prime = 0x01000193; + u32 hash = lastValue; + size_t i; + + const char *chardata = data; + + for (i = 0; i < len; i++) + { + hash = hash ^ chardata[i]; + hash = hash * fnv1a_prime; + } + + return hash; +} + +u32 +hash32 (const void *data, size_t len) +{ + // Standard FNV 32-bit offset: see http://www.isthe.com/chongo/tech/comp/fnv/#FNV-param + const u32 fnv1a_offset = 0x811C9DC5; + return cumulative_hash32 (data, len, fnv1a_offset); +} + +u64 +cumulative_hash64 (const void *data, size_t len, u64 lastValue) +{ + // Standard FNV 64-bit prime: see http://www.isthe.com/chongo/tech/comp/fnv/#FNV-param + const u64 fnv1a_prime = 0x00000100000001B3ULL; + u64 hash = lastValue; + const char *chardata = data; + size_t i; + + for (i = 0; i < len; i++) + { + hash = hash ^ chardata[i]; + hash = hash * fnv1a_prime; + } + + return hash; +} + +u64 +hash64 (const void *data, size_t len) +{ + // Standard FNV 64-bit offset: see http://www.isthe.com/chongo/tech/comp/fnv/#FNV-param + const u64 fnv1a_offset = 0xCBF29CE484222325ULL; + return cumulative_hash64 (data, len, fnv1a_offset); +} + +void +hicn_packet_dump (uint8_t * buffer, size_t len) +{ + int i; + unsigned char buff[17]; + unsigned char *pc = (unsigned char *) buffer; + + // Output description if given. + if (len == 0) + { + printf (" ZERO LENGTH\n"); + return; + } + + // Process every byte in the data. + for (i = 0; i < len; i++) + { + // Multiple of 16 means new line (with line offset). + + if ((i % 16) == 0) + { + // Just don't print ASCII for the zeroth line. + if (i != 0) + printf (" %s\n", buff); + + // Output the offset. + printf (" %04x ", i); + } + + // Now the hex code for the specific character. + printf (" %02x", pc[i]); + + // And store a printable ASCII character for later. + if ((pc[i] < 0x20) || (pc[i] > 0x7e)) + buff[i % 16] = '.'; + else + buff[i % 16] = pc[i]; + buff[(i % 16) + 1] = '\0'; + } + + // Pad out last line if not exactly 16 characters. + while ((i % 16) != 0) + { + printf (" "); + i++; + } + + // And print the final ASCII bit. + printf (" %s\n", buff); +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/common.h b/lib/src/common.h new file mode 100755 index 000000000..9ddbdeb09 --- /dev/null +++ b/lib/src/common.h @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file common.c + * @brief Common interfaces abstracting low-level platform. + * details. + * + * The role of this header file is to provide an uniform interface to the + * different platform on top of which we build the hICN interface: + * - syntax helpers + * - IP address management + * - protocol definition + * - ... + * + * The rationale is to leverage as much as possible platform-specific code, + * however some level of harmonization is needed to build code on top. Whenever + * possible, we align to VPP structure and naming. + */ + +#ifndef HICN_COMMON_H +#define HICN_COMMON_H + +#include +#include + +/* Concise type definitions */ + +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; + +/* + * Code annotations + * + * NOTE: these are defined by default in VPP. + */ + +#ifndef HICN_VPP_PLUGIN + +#define PREDICT_FALSE(x) (x) +#define PREDICT_TRUE(x) (x) +#define always_inline static inline +#define static_always_inline static inline +#define STRUCT_SIZE_OF(type, member) sizeof(((type *)0)->member) +#define ASSERT + +#define STATIC_ASSERT(x) + +#endif /* ! HICN_VPP_PLUGIN */ + +/* + * IP address types + */ + +#ifdef HICN_VPP_PLUGIN + +#include // ip4_address_t +#include // ip6_address_t + +#else + +#include + +typedef union +{ + u32 as_u32; + struct in_addr as_inaddr; +} ip4_address_t; + +typedef union +{ + u64 as_u64[2]; + u32 as_u32[4]; + u8 as_u8[16]; + struct in6_addr as_in6addr; +} ip6_address_t; + +typedef union +{ + struct + { + u32 pad[3]; + ip4_address_t ip4; + }; + ip6_address_t ip6; +} ip46_address_t; + +#define ip46_address_is_ip4(ip46) (((ip46)->pad[0] | (ip46)->pad[1] | (ip46)->pad[2]) == 0) + +#endif /* ! HICN_VPP_PLUGIN */ + +/** + * @brief Returns the family of an IP address + * @param [in] ip_address - IP address in presentation format + * @return AF_INET or AF_INET6 if successful, -1 otherwise + */ +int get_addr_family (const char *ip_address); + +/* + * Checksum computation + * + * NOTE: VPP provides efficient (incremental) checksum computations + * that we reuse, and provide alternative implementation otherwise. + */ + +#ifndef HICN_VPP_PLUGIN + +typedef u16 ip_csum_t; + +/* + * Checksum update (incremental and non-incremental) + * + * Those functions are already defined in VPP in vnet/ip/ip_packet.h, and we + * borrow this code here. + */ + +static_always_inline u16 +ip_csum_fold (ip_csum_t c) +{ + /* Reduce to 16 bits. */ +#if 0 // uword_bits == 64 + c = (c & (ip_csum_t) 0xffffffff) + (c >> (ip_csum_t) 32); + c = (c & 0xffff) + (c >> 16); +#endif + + c = (c & 0xffff) + (c >> 16); + c = (c & 0xffff) + (c >> 16); + + return c; +} + +static_always_inline ip_csum_t +ip_csum_with_carry (ip_csum_t sum, ip_csum_t x) +{ + ip_csum_t t = sum + x; + return t + (t < x); +} + +/* Update checksum changing field at even byte offset from x -> 0. */ +static_always_inline ip_csum_t +ip_csum_add_even (ip_csum_t c, ip_csum_t x) +{ + ip_csum_t d; + + d = c - x; + + /* Fold in carry from high bit. */ + d -= d > c; + + return d; +} + +/* Update checksum changing field at even byte offset from 0 -> x. */ +static_always_inline ip_csum_t +ip_csum_sub_even (ip_csum_t c, ip_csum_t x) +{ + return ip_csum_with_carry (c, x); +} + +u32 cumulative_hash32 (const void *data, size_t len, u32 lastValue); +u32 hash32 (const void *data, size_t len); +u64 cumulative_hash64 (const void *data, size_t len, u64 lastValue); +u64 hash64 (const void *data, size_t len); +void hicn_packet_dump (uint8_t * buffer, size_t len); + +#endif /* ! HICN_VPP_PLUGIN */ + +/** + * @brief Computes buffer checksum + * @param [in] addr - Pointer to buffer start + * @param [in] size - Size of buffer + * @param [in] init - Checksum initial value + * @return Checksum of specified buffer + */ +always_inline u16 +csum (const void *addr, size_t size, u16 init) +{ + u32 sum = init; + const u16 *bytes = (u16 *) addr; + + while (size > 1) + { + sum += *bytes++; + size -= sizeof (u16); + } + if (size) + { + sum += *(const u8 *) bytes; + } + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + + return (u16) ~ sum; +} + +/* + * Useful aliases + */ + +/* Symmetry with IPPROTO_ICMPV6 */ +#define IPPROTO_ICMPV4 IPPROTO_ICMP + +/* + * Query IP version from packet (either 4 or 6) + * (version is located as same offsets in both protocol headers) + */ +#define HICN_IP_VERSION(packet) ((hicn_header_t *)packet)->v4.ip.version + +#endif /* HICN_COMMON_H */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/compat.c b/lib/src/compat.c new file mode 100755 index 000000000..7d9eef025 --- /dev/null +++ b/lib/src/compat.c @@ -0,0 +1,1177 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file compat.c + * @brief Implementation of the compatibility layer. + */ + +#include +#include // memset +#include // offsetof + +#include "common.h" +#include "compat.h" +#include "error.h" +#include "header.h" +#include "name.h" +#include "ops.h" + +#define member_size(type, member) sizeof(((type *)0)->member) +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) + +#define HICN_NAME_COMPONENT_SIZE 2 + +int +hicn_packet_get_format (const hicn_header_t * h, hicn_format_t * format) +{ + *format = HF_UNSPEC; + + switch (HICN_IP_VERSION (h)) + { + case 4: + switch (h->v4.ip.protocol) + { + case IPPROTO_TCP: + if (h->v4.tcp.flags & AH_FLAG) + *format = HF_INET_TCP_AH; + else + *format = HF_INET_TCP; + break; + case IPPROTO_ICMP: + *format = HF_INET_ICMP; + break; + default: + return HICN_LIB_ERROR_NOT_HICN; + } + break; + case 6: + switch (h->v6.ip.nxt) + { + case IPPROTO_TCP: + if (h->v6.tcp.flags & AH_FLAG) + *format = HF_INET6_TCP_AH; + else + *format = HF_INET6_TCP; + break; + case IPPROTO_ICMPV6: + *format = HF_INET6_ICMP; + break; + default: + return HICN_LIB_ERROR_NOT_HICN; + } + break; + default: + return HICN_LIB_ERROR_NOT_HICN; + } + + return HICN_LIB_ERROR_NONE; +} + +/** + * @brief Convert (former) hICN format into (newer) hICN type + * @param [in] format - hICN format + * @return hICN type, all zero'ed if type is unknown + */ +hicn_type_t +hicn_format_to_type (hicn_format_t format) +{ + switch (format) + { + case HF_INET_TCP: + return (hicn_type_t) + { + .l4 = IPPROTO_NONE,.l3 = IPPROTO_NONE,.l2 = IPPROTO_TCP,.l1 = + IPPROTO_IP}; + case HF_INET6_TCP: + return (hicn_type_t) + { + .l4 = IPPROTO_NONE,.l3 = IPPROTO_NONE,.l2 = IPPROTO_TCP,.l1 = + IPPROTO_IPV6}; + case HF_INET_ICMP: + return (hicn_type_t) + { + .l4 = IPPROTO_NONE,.l3 = IPPROTO_NONE,.l2 = IPPROTO_ICMP,.l1 = + IPPROTO_IP}; + case HF_INET6_ICMP: + return (hicn_type_t) + { + .l4 = IPPROTO_NONE,.l3 = IPPROTO_NONE,.l2 = IPPROTO_ICMPV6,.l1 = + IPPROTO_IPV6}; + case HF_INET_TCP_AH: + return (hicn_type_t) + { + .l4 = IPPROTO_NONE,.l3 = IPPROTO_AH,.l2 = IPPROTO_TCP,.l1 = IPPROTO_IP}; + case HF_INET6_TCP_AH: + return (hicn_type_t) + { + .l4 = IPPROTO_NONE,.l3 = IPPROTO_AH,.l2 = IPPROTO_TCP,.l1 = + IPPROTO_IPV6}; + case HF_INET_ICMP_AH: + return (hicn_type_t) + { + .l4 = IPPROTO_NONE,.l3 = IPPROTO_AH,.l2 = IPPROTO_ICMP,.l1 = + IPPROTO_IP}; + case HF_INET6_ICMP_AH: + return (hicn_type_t) + { + .l4 = IPPROTO_NONE,.l3 = IPPROTO_AH,.l2 = IPPROTO_ICMPV6,.l1 = + IPPROTO_IPV6}; + default: + break; + } + return (hicn_type_t) + { + { + IPPROTO_NONE} + }; +} + +/** + * @brief Parse hICN header and return hICN type + * @param [in] h - hICN header + * @param [out] format - hICN type + * @return hICN error code + * + * This function is used to wrap old API calls to new ones + */ +hicn_type_t +hicn_header_to_type (const hicn_header_t * h) +{ + hicn_format_t format; + hicn_packet_get_format (h, &format); + return hicn_format_to_type (format); +} + +int +hicn_packet_init_header (hicn_format_t format, hicn_header_t * packet) +{ + hicn_type_t type = hicn_format_to_type (format); + return hicn_ops_vft[type.l1]->init_packet_header (type, &packet->protocol); +} + +int +hicn_packet_compute_checksum (hicn_format_t format, hicn_header_t * h) +{ + hicn_type_t type = hicn_format_to_type (format); + return hicn_ops_vft[type.l1]->update_checksums (type, &h->protocol, 0, 0); +} + +int +hicn_packet_compute_header_checksum (hicn_format_t format, hicn_header_t * h, + u16 init_sum) +{ + hicn_type_t type = hicn_format_to_type (format); + /* payload_length == ~0: ignore payload */ + return hicn_ops_vft[type.l1]->update_checksums (type, &h->protocol, + init_sum, ~0); +} + +int +hicn_packet_check_integrity (hicn_format_t format, hicn_header_t * h) +{ + hicn_type_t type = hicn_format_to_type (format); + return hicn_ops_vft[type.l1]->verify_checksums (type, &h->protocol, 0, 0); +} + +int +hicn_packet_get_header_length_from_format (hicn_format_t format, + size_t * header_length) +{ + *header_length = _is_ipv4 (format) * IPV4_HDRLEN; + *header_length += _is_ipv6 (format) * IPV6_HDRLEN; + *header_length += _is_icmp (format) * ICMP_HDRLEN; + *header_length += _is_tcp (format) * TCP_HDRLEN; + *header_length += _is_ah (format) * AH_HDRLEN; + + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_get_header_length (hicn_format_t format, const hicn_header_t * h, + size_t * header_length) +{ + hicn_packet_get_header_length_from_format (format, header_length); + int is_ah = _is_ah (format); + int is_ipv4 = _is_ipv4 (format); + int is_ipv6 = _is_ipv6 (format); + // The signature payload is expressed as number of 32 bits words + *header_length += (is_ah * is_ipv4) * (h->v4ah.ah.payloadlen) << 2; + *header_length += (is_ah * is_ipv6) * (h->v6ah.ah.payloadlen) << 2; + + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_get_payload_length (hicn_format_t format, const hicn_header_t * h, + size_t * payload_length) +{ + hicn_type_t type = hicn_format_to_type (format); + return hicn_ops_vft[type.l1]->get_payload_length (type, &h->protocol, + payload_length); +} + +int +hicn_packet_set_payload_length (hicn_format_t format, hicn_header_t * h, + size_t payload_length) +{ + hicn_type_t type = hicn_format_to_type (format); + return hicn_ops_vft[type.l1]->set_payload_length (type, &h->protocol, + payload_length); +} + +int +hicn_packet_compare (const hicn_header_t * packet1, + const hicn_header_t * packet2) +{ + hicn_type_t type1 = hicn_header_to_type (packet1); + hicn_type_t type2 = hicn_header_to_type (packet2); + + size_t len1, len2; + int rc; + + if (type1.as_u32 != type2.as_u32) + return HICN_LIB_ERROR_UNEXPECTED; + + rc = hicn_ops_vft[type1.l1]->get_length (type1, &packet1->protocol, &len1); + if (PREDICT_FALSE (rc < 0)) + return HICN_LIB_ERROR_UNEXPECTED; + + rc = hicn_ops_vft[type2.l1]->get_length (type2, &packet2->protocol, &len2); + if (PREDICT_FALSE (rc < 0)) + return HICN_LIB_ERROR_UNEXPECTED; + + if (len1 != len2) + return HICN_LIB_ERROR_UNEXPECTED; + + return memcmp ((u8 *) packet1, (u8 *) packet2, len1); + +} + +int +hicn_packet_get_name (hicn_format_t format, const hicn_header_t * h, + hicn_name_t * name, u8 is_interest) +{ + hicn_type_t type = hicn_format_to_type (format); + + if (is_interest) + return hicn_ops_vft[type.l1]->get_interest_name (type, &h->protocol, + name); + else + return hicn_ops_vft[type.l1]->get_data_name (type, &h->protocol, name); +} + +int +hicn_packet_set_name (hicn_format_t format, hicn_header_t * h, + const hicn_name_t * name, u8 is_interest) +{ + hicn_type_t type = hicn_format_to_type (format); + +#ifndef HICN_VPP_PLUGIN + if (name->type & HNT_IOV) + return HICN_LIB_ERROR_NOT_IMPLEMENTED; +#endif /* HICN_VPP_PLUGIN */ + + if (is_interest) + return hicn_ops_vft[type.l1]->set_interest_name (type, &h->protocol, + name); + else + return hicn_ops_vft[type.l1]->set_data_name (type, &h->protocol, name); +} + +int +hicn_packet_set_payload (hicn_format_t format, hicn_header_t * h, + const u8 * payload, u16 payload_length) +{ + hicn_type_t type = hicn_format_to_type (format); + size_t header_length; + int rc; + + rc = + hicn_ops_vft[type.l1]->get_header_length (type, &h->protocol, + &header_length); + if (rc < 0) + return rc; + + memcpy ((u8 *) h + header_length, payload, payload_length); + + return hicn_ops_vft[type.l1]->set_payload_length (type, &h->protocol, + payload_length); +} + +int +hicn_packet_get_payload (hicn_format_t format, const hicn_header_t * h, + u8 ** payload, size_t * payload_size, bool hard_copy) +{ + size_t header_length, payload_length; + int rc; + hicn_type_t type = hicn_format_to_type (format); + + rc = + hicn_ops_vft[type.l1]->get_header_length (type, &h->protocol, + &header_length); + if (rc < 0) + return rc; + + rc = + hicn_ops_vft[type.l1]->get_payload_length (type, &h->protocol, + &payload_length); + if (rc < 0) + return rc; + + if (hard_copy) + { + memcpy (payload, (u8 *) h + header_length, payload_length); + } + else + { + *payload = (u8 *) h + header_length; + } + + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_get_locator (hicn_format_t format, const hicn_header_t * h, + ip_address_t * ip_address, bool is_interest) +{ + const void *locator; + int is_ipv4 = (format & HFO_INET); + int is_ipv6 = (format & HFO_INET6) >> 1; + + if (is_ipv4) + { + locator = is_interest ? &h->v4.ip.saddr : &h->v4.ip.daddr; + ip_address->family = AF_INET; + ip_address->prefix_len = IPV4_ADDR_LEN_BITS; + } + else if (is_ipv6) + { + locator = is_interest ? &h->v6.ip.saddr : &h->v6.ip.daddr; + ip_address->family = AF_INET6; + ip_address->prefix_len = IPV6_ADDR_LEN_BITS; + } + else + { + return HICN_LIB_ERROR_NOT_IMPLEMENTED; + } + + memcpy (ip_address->buffer, locator, ip_address_len (ip_address)); + + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_set_locator (hicn_format_t format, hicn_header_t * h, + const ip_address_t * ip_address, bool is_interest) +{ + void *locator; + int is_ipv4 = (format & HFO_INET); + int is_ipv6 = (format & HFO_INET6) >> 1; + + if (is_ipv6) + { + locator = is_interest ? &h->v6.ip.saddr : &h->v6.ip.daddr; + } + else if (is_ipv4) + { + locator = is_interest ? &h->v4.ip.saddr : &h->v4.ip.daddr; + } + else + { + return HICN_LIB_ERROR_INVALID_PARAMETER; + } + + memcpy (locator, ip_address->buffer, ip_address_len (ip_address)); + + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_get_signature_size (hicn_format_t format, const hicn_header_t * h, + size_t * bytes) +{ + hicn_type_t type = hicn_format_to_type (format); + return hicn_ops_vft[type.l1]->get_signature_size (type, &h->protocol, + bytes); +} + +int +hicn_packet_set_signature_size (hicn_format_t format, hicn_header_t * h, + size_t bytes) +{ + hicn_type_t type = hicn_format_to_type (format); + return hicn_ops_vft[type.l1]->set_signature_size (type, &h->protocol, + bytes); +} + +int +hicn_packet_set_signature_timestamp (hicn_format_t format, hicn_header_t * h, + uint64_t signature_timestamp) +{ + hicn_type_t type = hicn_format_to_type (format); + return hicn_ops_vft[type.l1]->set_signature_timestamp (type, &h->protocol, + signature_timestamp); +} + +int +hicn_packet_get_signature_timestamp (hicn_format_t format, const hicn_header_t * h, + uint64_t *signature_timestamp) +{ + hicn_type_t type = hicn_format_to_type (format); + return hicn_ops_vft[type.l1]->get_signature_timestamp (type, &h->protocol, + signature_timestamp); +} + +int +hicn_packet_set_validation_algorithm (hicn_format_t format, hicn_header_t * h, + uint8_t validation_algorithm) +{ + hicn_type_t type = hicn_format_to_type (format); + return hicn_ops_vft[type.l1]->set_validation_algorithm (type, &h->protocol, + validation_algorithm); +} + +int +hicn_packet_get_validation_algorithm (hicn_format_t format, const hicn_header_t * h, + uint8_t * validation_algorithm) +{ + hicn_type_t type = hicn_format_to_type (format); + return hicn_ops_vft[type.l1]->get_validation_algorithm (type, &h->protocol, + validation_algorithm); +} + +int +hicn_packet_set_key_id (hicn_format_t format, hicn_header_t * h, + uint8_t *key_id) +{ + hicn_type_t type = hicn_format_to_type (format); + return hicn_ops_vft[type.l1]->set_key_id (type, &h->protocol, + key_id); +} + +int +hicn_packet_get_key_id (hicn_format_t format, hicn_header_t * h, + uint8_t ** key_id, uint8_t *key_id_length) +{ + hicn_type_t type = hicn_format_to_type (format); + return hicn_ops_vft[type.l1]->get_key_id (type, &h->protocol, + key_id, key_id_length); +} + +int +hicn_packet_get_hoplimit (const hicn_header_t * h, u8 * hops) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + *hops = h->v6.ip.hlim; + break; + case 4: + *hops = h->v4.ip.ttl; + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_set_hoplimit (hicn_header_t * h, u8 hops) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + h->v6.ip.hlim = hops; + break; + case 4: + h->v4.ip.ttl = hops; + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + + return HICN_LIB_ERROR_NONE; +} + + +int +hicn_packet_get_lifetime (const hicn_header_t * h, u32 * lifetime) +{ + hicn_type_t type = hicn_header_to_type (h); + return hicn_ops_vft[type.l1]->get_lifetime (type, &h->protocol, + (hicn_lifetime_t *) lifetime); +} + +int +hicn_packet_set_lifetime (hicn_header_t * h, u32 lifetime) +{ + hicn_type_t type = hicn_header_to_type (h); + return hicn_ops_vft[type.l1]->set_lifetime (type, &h->protocol, + (hicn_lifetime_t) lifetime); +} + +int +hicn_packet_get_reserved_bits (const hicn_header_t * h, u8 * reserved_bits) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + *reserved_bits = h->v6.tcp.reserved; + break; + case 4: + *reserved_bits = h->v4.tcp.reserved; + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_set_reserved_bits (hicn_header_t * h, u8 reserved_bits) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + h->v6.tcp.reserved = reserved_bits; + break; + case 4: + h->v4.tcp.reserved = reserved_bits; + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_get_payload_type (const hicn_header_t * h, + hicn_payload_type_t * payload_type) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + *payload_type = ((h->v6.tcp.flags & TCP_FLAG_URG) == TCP_FLAG_URG); + break; + case 4: + *payload_type = ((h->v4.tcp.flags & TCP_FLAG_URG) == TCP_FLAG_URG); + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + + if (*payload_type == HPT_UNSPEC) + { + return HICN_LIB_ERROR_UNEXPECTED; + } + + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_set_payload_type (hicn_header_t * h, + hicn_payload_type_t payload_type) +{ + if (payload_type != HPT_DATA && payload_type != HPT_MANIFEST) + { + return HICN_LIB_ERROR_UNEXPECTED; + } + + switch (HICN_IP_VERSION (h)) + { + case 6: + if (payload_type) + h->v6.tcp.flags = h->v6.tcp.flags | TCP_FLAG_URG; + else + h->v6.tcp.flags = h->v6.tcp.flags & ~TCP_FLAG_URG; + break; + case 4: + if (payload_type) + h->v4.tcp.flags = h->v4.tcp.flags | TCP_FLAG_URG; + else + h->v4.tcp.flags = h->v4.tcp.flags & ~TCP_FLAG_URG; + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_set_syn (hicn_header_t * h) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + h->v6.tcp.flags = h->v6.tcp.flags | TCP_FLAG_SYN; + break; + case 4: + h->v4.tcp.flags = h->v4.tcp.flags | TCP_FLAG_SYN; + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_reset_syn (hicn_header_t * h) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + h->v6.tcp.flags = h->v6.tcp.flags & ~TCP_FLAG_SYN; + break; + case 4: + h->v4.tcp.flags = h->v4.tcp.flags & ~TCP_FLAG_SYN; + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_test_syn (const hicn_header_t * h, bool * flag) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + *flag = h->v6.tcp.flags & TCP_FLAG_SYN; + break; + case 4: + *flag = h->v4.tcp.flags & TCP_FLAG_SYN; + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_set_ack (hicn_header_t * h) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + h->v6.tcp.flags = h->v6.tcp.flags | TCP_FLAG_ACK; + break; + case 4: + h->v4.tcp.flags = h->v4.tcp.flags | TCP_FLAG_ACK; + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_reset_ack (hicn_header_t * h) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + h->v6.tcp.flags = h->v6.tcp.flags & ~TCP_FLAG_ACK; + break; + case 4: + h->v4.tcp.flags = h->v4.tcp.flags & ~TCP_FLAG_ACK; + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_test_ack (const hicn_header_t * h, bool * flag) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + *flag = h->v6.tcp.flags & TCP_FLAG_ACK; + break; + case 4: + *flag = h->v4.tcp.flags & TCP_FLAG_ACK; + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_set_rst (hicn_header_t * h) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + h->v6.tcp.flags = h->v6.tcp.flags | TCP_FLAG_RST; + break; + case 4: + h->v4.tcp.flags = h->v4.tcp.flags | TCP_FLAG_RST; + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_reset_rst (hicn_header_t * h) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + h->v6.tcp.flags = h->v6.tcp.flags & ~TCP_FLAG_RST; + break; + case 4: + h->v4.tcp.flags = h->v4.tcp.flags & ~TCP_FLAG_RST; + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_test_rst (const hicn_header_t * h, bool * flag) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + *flag = h->v6.tcp.flags & TCP_FLAG_RST; + break; + case 4: + *flag = h->v4.tcp.flags & TCP_FLAG_RST; + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_set_fin (hicn_header_t * h) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + h->v6.tcp.flags = h->v6.tcp.flags | TCP_FLAG_FIN; + break; + case 4: + h->v4.tcp.flags = h->v4.tcp.flags | TCP_FLAG_FIN; + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_reset_fin (hicn_header_t * h) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + h->v6.tcp.flags = h->v6.tcp.flags & ~TCP_FLAG_FIN; + break; + case 4: + h->v4.tcp.flags = h->v4.tcp.flags & ~TCP_FLAG_FIN; + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_test_fin (const hicn_header_t * h, bool * flag) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + *flag = h->v6.tcp.flags & TCP_FLAG_FIN; + break; + case 4: + *flag = h->v4.tcp.flags & TCP_FLAG_FIN; + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_set_ece (hicn_header_t * h) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + h->v6.tcp.flags = h->v6.tcp.flags | TCP_FLAG_ECE; + break; + case 4: + h->v4.tcp.flags = h->v4.tcp.flags | TCP_FLAG_ECE; + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_reset_ece (hicn_header_t * h) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + h->v6.tcp.flags = h->v6.tcp.flags & ~TCP_FLAG_ECE; + break; + case 4: + h->v4.tcp.flags = h->v4.tcp.flags & ~TCP_FLAG_ECE; + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_test_ece (const hicn_header_t * h, bool * flag) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + *flag = h->v6.tcp.flags & TCP_FLAG_ECE; + break; + case 4: + *flag = h->v4.tcp.flags & TCP_FLAG_ECE; + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_set_src_port (hicn_header_t * h, u16 src_port) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + h->v6.tcp.sport = htons (src_port); + break; + case 4: + h->v4.tcp.sport = htons (src_port); + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_get_src_port (const hicn_header_t * h, u16 * src_port) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + *src_port = ntohs (h->v6.tcp.sport); + break; + case 4: + *src_port = ntohs (h->v4.tcp.sport); + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_set_dst_port (hicn_header_t * h, u16 dst_port) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + h->v6.tcp.dport = htons (dst_port); + break; + case 4: + h->v4.tcp.dport = htons (dst_port); + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_get_dst_port (const hicn_header_t * h, u16 * dst_port) +{ + switch (HICN_IP_VERSION (h)) + { + case 6: + *dst_port = ntohs (h->v6.tcp.dport); + break; + case 4: + *dst_port = ntohs (h->v4.tcp.dport); + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + return HICN_LIB_ERROR_NONE; +} + +int +hicn_packet_copy_header (hicn_format_t format, const hicn_header_t * packet, + hicn_header_t * destination, bool copy_ah) +{ + size_t header_length = _is_ipv4 (format) * IPV4_HDRLEN; + header_length += _is_ipv6 (format) * IPV6_HDRLEN; + header_length += _is_icmp (format) * ICMP_HDRLEN; + header_length += _is_tcp (format) * TCP_HDRLEN; + header_length += _is_ah (format) * copy_ah * AH_HDRLEN; + + memcpy (destination, packet, header_length); + + return HICN_LIB_ERROR_NONE; +} + +#define _INTEREST 1 +#define _DATA 0 + +/* Interest */ + +int +hicn_interest_get_name (hicn_format_t format, const hicn_header_t * interest, + hicn_name_t * name) +{ + return hicn_packet_get_name (format, interest, name, _INTEREST); +} + +int +hicn_interest_set_name (hicn_format_t format, hicn_header_t * interest, + const hicn_name_t * name) +{ + int ret_err = hicn_packet_reset_ece (interest); //interest packet -> ece flag unset + if (ret_err < 0) + return HICN_LIB_ERROR_UNEXPECTED; + return hicn_packet_set_name (format, interest, name, _INTEREST); +} + +int +hicn_interest_get_locator (hicn_format_t format, + const hicn_header_t * interest, + ip_address_t * ip_address) +{ + return hicn_packet_get_locator (format, interest, ip_address, _INTEREST); +} + +int +hicn_interest_set_locator (hicn_format_t format, hicn_header_t * interest, + const ip_address_t * ip_address) +{ + return hicn_packet_set_locator (format, interest, ip_address, _INTEREST); +} + +int +hicn_interest_compare (const hicn_header_t * interest_1, + const hicn_header_t * interest_2) +{ + return hicn_packet_compare (interest_1, interest_2); +} + +int +hicn_interest_get_lifetime (const hicn_header_t * interest, u32 * lifetime) +{ + return hicn_packet_get_lifetime (interest, lifetime); +} + +int +hicn_interest_set_lifetime (hicn_header_t * interest, u32 lifetime) +{ + return hicn_packet_set_lifetime (interest, lifetime); +} + +int +hicn_interest_get_header_length (hicn_format_t format, + const hicn_header_t * interest, + size_t * header_length) +{ + return hicn_packet_get_header_length (format, interest, header_length); +} + +int +hicn_interest_get_payload_length (hicn_format_t format, + const hicn_header_t * interest, + size_t * payload_length) +{ + return hicn_packet_get_payload_length (format, interest, payload_length); +} + +int +hicn_interest_get_payload (hicn_format_t format, + const hicn_header_t * interest, u8 ** payload, + size_t * payload_size, bool hard_copy) +{ + return hicn_packet_get_payload (format, interest, payload, payload_size, + hard_copy); +} + +int +hicn_interest_set_payload (hicn_format_t format, hicn_header_t * interest, + const u8 * payload, size_t payload_length) +{ + return hicn_packet_set_payload (format, interest, payload, payload_length); +} + +int +hicn_interest_reset_for_hash (hicn_format_t format, hicn_header_t * packet) +{ + hicn_type_t type = hicn_format_to_type (format); + return hicn_ops_vft[type.l1]->reset_interest_for_hash (type, + &packet->protocol); +} + +/* Data */ + +int +hicn_data_get_name (hicn_format_t format, const hicn_header_t * data, + hicn_name_t * name) +{ + return hicn_packet_get_name (format, data, name, _DATA); +} + +int +hicn_data_set_name (hicn_format_t format, hicn_header_t * data, + hicn_name_t * name) +{ + int ret_err = hicn_packet_set_ece (data); //data packet -> ece flag set + if (ret_err < 0) + return HICN_LIB_ERROR_UNEXPECTED; + return hicn_packet_set_name (format, data, name, _DATA); +} + +int +hicn_data_get_locator (hicn_format_t format, const hicn_header_t * data, + ip_address_t * ip_address) +{ + return hicn_packet_get_locator (format, data, ip_address, _DATA); +} + +int +hicn_data_set_locator (hicn_format_t format, hicn_header_t * data, + const ip_address_t * ip_address) +{ + return hicn_packet_set_locator (format, data, ip_address, _DATA); +} + +int +hicn_data_compare (const hicn_header_t * data_1, const hicn_header_t * data_2) +{ + return hicn_packet_compare (data_1, data_2); +} + +int +hicn_data_get_expiry_time (const hicn_header_t * data, u32 * expiry_time) +{ + return hicn_packet_get_lifetime (data, expiry_time); +} + +int +hicn_data_set_expiry_time (hicn_header_t * data, u32 expiry_time) +{ + return hicn_packet_set_lifetime (data, (hicn_lifetime_t) expiry_time); +} + +int +hicn_data_get_header_length (hicn_format_t format, hicn_header_t * data, + size_t * header_length) +{ + return hicn_packet_get_header_length (format, data, header_length); +} + +int +hicn_data_get_payload_length (hicn_format_t format, + const hicn_header_t * data, + size_t * payload_length) +{ + return hicn_packet_get_payload_length (format, data, payload_length); +} + +int +hicn_data_set_payload_type (hicn_header_t * data, + hicn_payload_type_t payload_type) +{ + return hicn_packet_set_payload_type (data, payload_type); +} + +int +hicn_data_get_payload_type (const hicn_header_t * data, + hicn_payload_type_t * payload_type) +{ + return hicn_packet_get_payload_type (data, payload_type); +} + +int +hicn_data_get_path_label (const hicn_header_t * data, u32 * path_label) +{ + hicn_type_t type = hicn_header_to_type (data); + return hicn_ops_vft[type.l1]->get_data_pathlabel (type, &data->protocol, + path_label); +} + +int +hicn_data_set_path_label (hicn_header_t * data, u32 path_label) +{ + hicn_type_t type = hicn_header_to_type (data); + return hicn_ops_vft[type.l1]->set_data_pathlabel (type, &data->protocol, + path_label); +} + +int +hicn_data_set_payload (hicn_format_t format, hicn_header_t * data, + const u8 * payload, size_t payload_length) +{ + return hicn_packet_set_payload (format, data, payload, payload_length); +} + +int +hicn_data_get_payload (hicn_format_t format, const hicn_header_t * data, + u8 ** payload, size_t * payload_size, bool hard_copy) +{ + return hicn_packet_get_payload (format, data, payload, payload_size, + hard_copy); +} + +int +hicn_data_reset_for_hash (hicn_format_t format, hicn_header_t * packet) +{ + hicn_type_t type = hicn_format_to_type (format); + return hicn_ops_vft[type.l1]->reset_interest_for_hash (type, + &packet->protocol); + +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/compat.h b/lib/src/compat.h new file mode 100755 index 000000000..1a1743de2 --- /dev/null +++ b/lib/src/compat.h @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file compat.h + * @brief Implementation of the compatibility layer. + * + * The structure of the core API has evolved to support operations of a variety + * of packet formats in addition to IPv4/TCP and IPv6/TCP, namely with the use + * of ICMP for signalization and AH headers for integrity. The new API format + * has been designed to scale better with the multiplicity of packet formats, + * and provide a unified interface on top. We maintain an interface for the + * former API in this file, which mainly acts as a wrapper on top of new calls. + */ +#ifndef HICN_COMPAT_H +#define HICN_COMPAT_H + +#include "common.h" +#include "header.h" +#include "name.h" + +/* HICN format options */ +#define HFO_INET 1 << 0 +#define HFO_INET6 1 << 1 +#define HFO_TCP 1 << 2 +#define HFO_ICMP 1 << 3 +#define HFO_AH 1 << 4 + +#define _is_ipv4(format) ((format & HFO_INET)) +#define _is_ipv6(format) ((format & HFO_INET6) >> 1) +#define _is_tcp(format) ((format & HFO_TCP) >> 2) +#define _is_icmp(format) ((format & HFO_ICMP) >> 3) +#define _is_ah(format) ((format & HFO_AH) >> 4) + +typedef enum +{ + HF_UNSPEC = 0, + HF_INET_TCP = HFO_INET | HFO_TCP, + HF_INET6_TCP = HFO_INET6 | HFO_TCP, + HF_INET_ICMP = HFO_INET | HFO_ICMP, + HF_INET6_ICMP = HFO_INET6 | HFO_ICMP, + HF_INET_TCP_AH = HFO_INET | HFO_TCP | HFO_AH, + HF_INET6_TCP_AH = HFO_INET6 | HFO_TCP | HFO_AH, + HF_INET_ICMP_AH = HFO_INET | HFO_ICMP | HFO_AH, + HF_INET6_ICMP_AH = HFO_INET6 | HFO_ICMP | HFO_AH +} hicn_format_t; + +/** + * Maximum Size of the hICN header (the effective size will depend on the + * underlying IP version) + */ +#define HICN_HDR_LEN sizeof(hicn_header_t) + +/** + * Minimum required header length to determine the type and length of a supposed + * hICN packet. + * This should be equal to the maximum value over all possible hICN packet + * formats, and less than the minimum possible IP packet size. + */ +#define HICN_V6_MIN_HDR_LEN 6 /* bytes */ +#define HICN_V4_MIN_HDR_LEN 4 /* bytes */ + +// #define HICN_MIN_HDR_LEN ((HICN_V6_MIN_HDR_LEN > HICN_V4_MIN_HDR_LEN) ? HICN_V6_MIN_HDR_LEN : HICN_V4_MIN_HDR_LEN) +#define HICN_MIN_HDR_LEN HICN_V6_MIN_HDR_LEN + +/** + * @brief Parse packet headers and return hICN format + * @param [in] format - hICN Format + * @param [in, out] packet - Buffer containing the hICN header to be initialized + * @return hICN error code + */ +int hicn_packet_init_header (hicn_format_t format, hicn_header_t * packet); + +/** + * @brief Parse packet headers and return hICN format + * @param [in] h - hICN header + * @param [out] format - hICN format + * @return hICN error code + */ +int hicn_packet_get_format (const hicn_header_t * packet, + hicn_format_t * format); + +/** + * @brief Update checksums in packet headers + * @param [in] format - hICN format + * @param [in,out] packet - packet header + * @return hICN error code + */ +int hicn_packet_compute_checksum (hicn_format_t format, + hicn_header_t * packet); + +/** + * @brief compute the checksum of the packet header, adding init_sum to the final value + * @param [in] format - hICN format + * @param [in,out] packet - packet header + * @param [in] init_sum - value to add to the final checksum + * @return hICN error code + */ +int hicn_packet_compute_header_checksum (hicn_format_t format, + hicn_header_t * packet, + u16 init_sum); + +/** + * @brief Verify checksums in packet headers + * @param [in] format - hICN format + * @param [in,out] packet - packet header + * @return hICN error code + */ +int hicn_packet_check_integrity (hicn_format_t format, + hicn_header_t * packet); + +// this is not accounted here +/** + * @brief Return total length of hicn headers (but signature payload) + * @param [in] format - hICN format + * @param [out] header_length - Total length of headers + * @return hICN error code + */ +int hicn_packet_get_header_length_from_format (hicn_format_t format, + size_t * header_length); + +/** + * @brief Return total length of hicn headers (before payload) + * @param [in] format - hICN format + * @param [in] packet - packet header + * @param [out] header_length - Total length of headers + * @return hICN error code + */ +int hicn_packet_get_header_length (hicn_format_t format, + const hicn_header_t * packet, + size_t * header_length); + +/** + * @brief Return payload length + * @param [in] format - hICN format + * @param [in] packet - packet header + * @param [out] payload_length - payload length + * @return hICN error code + */ +int hicn_packet_get_payload_length (hicn_format_t format, + const hicn_header_t * packet, + size_t * payload_length); + +/** + * @brief Sets payload length + * @param [in] format - hICN format + * @param [in,out] packet - packet header + * @param [in] payload_length - payload length + * @return hICN error code + */ +int hicn_packet_set_payload_length (hicn_format_t format, + hicn_header_t * packet, + const size_t payload_length); + +/** + * @brief Compare two hICN packets + * @param [in] packet_1 - First packet + * @param [in] packet_2 - Second packet + * @return 0 if both packets are considered equal, any other value otherwise. + */ +int hicn_packet_compare (const hicn_header_t * packet1, + const hicn_header_t * packet2); + +/** + * @brief Retrieve the name of an interest/data packet + * @param [in] format - hICN format + * @param [in] packet - packet header + * @param [out] name - name holding the result + * @param [in] is_interest - Flag to determine whether it is an interest (1) or + * data packet (0) + * @return hICN error code + */ +int hicn_packet_get_name (hicn_format_t format, const hicn_header_t * packet, + hicn_name_t * name, u8 is_interest); + +/** + * @brief Sets the name of an interest/data packet + * @param [in] format - hICN format + * @param [in,out] packet - packet header + * @param [in] name - name to set into packet + * @param [in] is_interest - Flag to determine whether it is an interest (1) or + * data packet (0) + * @return hICN error code + */ +int hicn_packet_set_name (hicn_format_t format, hicn_header_t * packet, + const hicn_name_t * name, u8 is_interest); + +/** + * @brief Sets the payload of a packet + * @param [in] format - hICN format + * @param [in,out] packet - packet header + * @param [in] payload - payload to set + * @param [in] payload_length - size of the payload to set + * @return hICN error code + * + * NOTE: + * - The buffer holding payload is assumed sufficiently large + * - This function updates header fields with the new length, but no checksum. + */ +int hicn_packet_set_payload (hicn_format_t format, hicn_header_t * packet, + const u8 * payload, u16 payload_length); + +/** + * @brief Retrieves the payload of a packet + * @param [in] format - hICN format + * @param [in] packet - packet header + * @param [out] payload - pointer to buffer for storing the result + * @param [out] payload_length - size of the retreived payload + * @param [in] hard_copy - Flag : if true (eg. 1), a copy of the payload is made + * into the payload buffer, otherwise (0) the pointer is changed to point to the payload offset in the packet. + * @return hICN error code + * + * NOTE: + * - The buffer holding payload is assumed sufficiently large + */ +int hicn_packet_get_payload (hicn_format_t format, + const hicn_header_t * packet, u8 ** payload, + size_t * payload_size, bool hard_copy); + +/** + * @brief Retrieve the locator of an interest / data packet + * @param [in] format - hICN format + * @param [in] packet - packet header + * @param [out] ip_address - retrieved locator + * @param [in] is_interest - Flag to determine whether it is an interest (1) or + * data packet (0) + * @return hICN error code + */ +int hicn_packet_get_locator (hicn_format_t format, + const hicn_header_t * packet, + ip_address_t * ip_address, bool is_interest); + +/** + * @brief Sets the locator of an interest / data packet + * @param [in] format - hICN format + * @param [in,out] packet - packet header + * @param [out] ip_address - retrieved locator + * @param [in] is_interest - Flag to determine whether it is an interest (1) or + * data packet (0) + * @return hICN error code + */ +int hicn_packet_set_locator (hicn_format_t format, hicn_header_t * packet, + const ip_address_t * ip_address, + bool is_interest); + +/** + * @brief Retrieves the signature size + * @param [in] format - hICN format + * @param [in] packet - packet header + * @param [out] bytes - Retrieved signature size + * @return hICN error code + */ +int hicn_packet_get_signature_size (hicn_format_t format, + const hicn_header_t * packet, + size_t * bytes); + +/** + * @brief Sets the signature size + * @param [in] format - hICN format + * @param [in] packet - packet header + * @param [in] bytes - Retrieved signature size + * @return hICN error code + */ +int hicn_packet_set_signature_size (hicn_format_t format, + hicn_header_t * packet, size_t bytes); + +/** + * @brief Sets the signature size + * @param [in] format - hICN format + * @param [in] packet - packet header + * @param [in] signature_timestamp - Signature timestamp to set + * @return hICN error code + */ +int hicn_packet_set_signature_timestamp (hicn_format_t format, hicn_header_t * h, + uint64_t signature_timestamp); + +/** + * @brief Sets the signature size + * @param [in] format - hICN format + * @param [in] packet - packet header + * @param [out] signature_timestamp - Retrieved signature timestamp + * @return hICN error code + */ +int hicn_packet_get_signature_timestamp (hicn_format_t format, const hicn_header_t * h, + uint64_t *signature_timestamp); + +/** + * @brief Sets the signature size + * @param [in] format - hICN format + * @param [in] packet - packet header + * @param [in] validation_algorithm - Validation algorithm to set + * @return hICN error code + */ +int hicn_packet_set_validation_algorithm (hicn_format_t format, hicn_header_t * h, + uint8_t validation_algorithm); + +/** + * @brief Sets the signature size + * @param [in] format - hICN format + * @param [in] packet - packet header + * @param [out] validation_algorithm - Retrieved validation algorithm + * @return hICN error code + */ +int hicn_packet_get_validation_algorithm (hicn_format_t format, const hicn_header_t * h, + uint8_t * validation_algorithm); + +/** + * @brief Sets the signature size + * @param [in] format - hICN format + * @param [in] packet - packet header + * @param [in] key_id - Key id to set + * @return hICN error code + */ +int hicn_packet_set_key_id (hicn_format_t format, hicn_header_t * h, + uint8_t *key_id); + +/** + * @brief Sets the signature size + * @param [in] format - hICN format + * @param [in] packet - packet header + * @param [out] key_id - Retrieved key id + * @return hICN error code + */ +int hicn_packet_get_key_id (hicn_format_t format, hicn_header_t * h, + uint8_t ** key_id, uint8_t *key_id_length); + +/** + * @brief Retrieves the packet hop limit + * @param [in] packet - packet header + * @param [out] hops - Retrieved hop limit + * @return hICN error code + */ +int hicn_packet_get_hoplimit (const hicn_header_t * packet, u8 * hops); + +/** + * @brief Sets the packet hop limit + * @param [in] packet - packet header + * @param [in] hops - Hop limit to set + * @return hICN error code + */ +int hicn_packet_set_hoplimit (hicn_header_t * packet, u8 hops); + +int hicn_packet_copy_header (hicn_format_t format, + const hicn_header_t * packet, + hicn_header_t * destination, bool copy_ah); + +int hicn_packet_get_lifetime (const hicn_header_t * packet, u32 * lifetime); +int hicn_packet_set_lifetime (hicn_header_t * packet, u32 lifetime); +int hicn_packet_get_reserved_bits (const hicn_header_t * packet, + u8 * reserved_bits); +int hicn_packet_set_reserved_bits (hicn_header_t * packet, + const u8 reserved_bits); +int hicn_packet_get_payload_type (const hicn_header_t * packet, + hicn_payload_type_t * payload_type); +int hicn_packet_set_payload_type (hicn_header_t * packet, + const hicn_payload_type_t payload_type); + +int hicn_packet_set_syn (hicn_header_t * packet); +int hicn_packet_reset_syn (hicn_header_t * packet); +int hicn_packet_test_syn (const hicn_header_t * packet, bool * flag); +int hicn_packet_set_ack (hicn_header_t * packet); +int hicn_packet_reset_ack (hicn_header_t * packet); +int hicn_packet_test_ack (const hicn_header_t * packet, bool * flag); +int hicn_packet_set_rst (hicn_header_t * packet); +int hicn_packet_reset_rst (hicn_header_t * packet); +int hicn_packet_test_rst (const hicn_header_t * packet, bool * flag); +int hicn_packet_set_fin (hicn_header_t * packet); +int hicn_packet_reset_fin (hicn_header_t * packet); +int hicn_packet_test_fin (const hicn_header_t * packet, bool * flag); +int hicn_packet_set_ece (hicn_header_t * packet); +int hicn_packet_reset_ece (hicn_header_t * packet); +int hicn_packet_test_ece (const hicn_header_t * packet, bool * flag); + +int hicn_packet_set_src_port (hicn_header_t * packet, u16 src_port); +int hicn_packet_get_src_port (const hicn_header_t * packet, u16 * src_port); +int hicn_packet_set_dst_port (hicn_header_t * packet, u16 dst_port); +int hicn_packet_get_dst_port (const hicn_header_t * packet, u16 * dst_port); + +/* Interest */ + +int hicn_interest_get_name (hicn_format_t format, + const hicn_header_t * interest, + hicn_name_t * name); +int hicn_interest_set_name (hicn_format_t format, hicn_header_t * interest, + const hicn_name_t * name); +int hicn_interest_get_locator (hicn_format_t format, + const hicn_header_t * interest, + ip_address_t * ip_address); +int hicn_interest_set_locator (hicn_format_t format, hicn_header_t * interest, + const ip_address_t * ip_address); +int hicn_interest_compare (const hicn_header_t * interest_1, + const hicn_header_t * interest_2); +int hicn_interest_set_lifetime (hicn_header_t * interest, u32 lifetime); +int hicn_interest_get_lifetime (const hicn_header_t * interest, + u32 * lifetime); +int hicn_interest_get_header_length (hicn_format_t format, + const hicn_header_t * interest, + size_t * header_length); +int hicn_interest_get_payload_length (hicn_format_t format, + const hicn_header_t * interest, + size_t * payload_length); +int hicn_interest_set_payload (hicn_format_t format, hicn_header_t * interest, + const u8 * payload, size_t payload_length); +int hicn_interest_get_payload (hicn_format_t format, + const hicn_header_t * interest, u8 ** payload, + size_t * payload_size, bool hard_copy); +int hicn_interest_reset_for_hash (hicn_format_t format, + hicn_header_t * packet); + +/* Data */ + +int hicn_data_get_name (hicn_format_t format, const hicn_header_t * data, + hicn_name_t * name); +int hicn_data_set_name (hicn_format_t format, hicn_header_t * data, + hicn_name_t * name); +int hicn_data_get_locator (hicn_format_t format, const hicn_header_t * data, + ip_address_t * ip_address); +int hicn_data_set_locator (hicn_format_t format, hicn_header_t * data, + const ip_address_t * ip_address); +int hicn_data_compare (const hicn_header_t * data_1, + const hicn_header_t * data_2); +int hicn_data_get_expiry_time (const hicn_header_t * data, u32 * expiry_time); +int hicn_data_set_expiry_time (hicn_header_t * data, u32 expiry_time); +int hicn_data_get_header_length (hicn_format_t format, hicn_header_t * data, + size_t * header_length); +int hicn_data_get_payload_length (hicn_format_t format, + const hicn_header_t * data, + size_t * payload_length); +int hicn_data_get_path_label (const hicn_header_t * data, u32 * path_label); +int hicn_data_set_path_label (hicn_header_t * data, u32 path_label); +int hicn_data_get_payload (hicn_format_t format, const hicn_header_t * data, + u8 ** payload, size_t * payload_size, + bool hard_copy); +int hicn_data_set_payload (hicn_format_t format, hicn_header_t * data, + const u8 * payload, size_t payload_length); +int hicn_data_get_payload_type (const hicn_header_t * data, + hicn_payload_type_t * payload_type); +int hicn_data_set_payload_type (hicn_header_t * data, + hicn_payload_type_t payload_type); +int hicn_data_reset_for_hash (hicn_format_t format, hicn_header_t * packet); + +#endif /* HICN_COMPAT_H */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/error.c b/lib/src/error.c new file mode 100755 index 000000000..865e2b47d --- /dev/null +++ b/lib/src/error.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file error.c + * @brief Implementation of error management functions. + */ + +#include "error.h" + +const char *HICN_LIB_ERROR_STRING[] = { +#define _(a,b,c) [b] = c, + foreach_libhicn_error +#undef _ +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/error.h b/lib/src/error.h new file mode 100755 index 000000000..3e027c4e5 --- /dev/null +++ b/lib/src/error.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file error.h + * @brief Error management functions. + */ +#ifndef HICN_ERROR_H +#define HICN_ERROR_H + +/****************************************************************************** + * Error definitions + ******************************************************************************/ + +#define foreach_libhicn_error \ +_(NONE, 0, "OK") \ +_(UNSPECIFIED, 128, "Unspecified Error") \ +_(NOT_IMPLEMENTED, 180, "Function not yet implemented") \ +_(NOT_HICN, 202, "Non hICN packet") \ +_(UNKNOWN_ADDRESS, 210, "Unknown address") \ +_(INVALID_PARAMETER, 220, "Invalid parameter") \ +_(INVALID_IP_ADDRESS, 221, "Invalid IP address") \ +_(CORRUPTED_PACKET, 222, "Corrupted packet ") \ +_(UNEXPECTED, 298, "Unexpected error") + +typedef enum +{ +#define _(a,b,c) HICN_LIB_ERROR_##a = (-b), + foreach_libhicn_error +#undef _ + HICN_LIB_N_ERROR, +} hicn_lib_error_t; + +extern const char *HICN_LIB_ERROR_STRING[]; + +#define hicn_strerror(errno) (char *)(HICN_LIB_ERROR_STRING[-errno]) + +#endif /* HICN_ERROR_H */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/header.h b/lib/src/header.h new file mode 100755 index 000000000..3864064f2 --- /dev/null +++ b/lib/src/header.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file header.h + * @brief hICN header data structures. + * details. + */ +#ifndef HICN_HEADER_H +#define HICN_HEADER_H + +#include "common.h" +#include "protocol.h" + + +typedef struct +{ + _ipv6_header_t ip; + union + { + _tcp_header_t tcp; + _icmp_header_t icmp; + _icmp_wldr_header_t wldr; + }; +} hicn_v6_hdr_t; + +typedef struct +{ + _ipv6_header_t ip; + union + { + struct + { + _tcp_header_t tcp; + _ah_header_t ah; + }; + struct + { + _icmp_header_t icmp; + _ah_header_t icmp_ah; + }; + }; +} hicn_v6ah_hdr_t; + +typedef struct +{ + _ipv4_header_t ip; + union + { + _tcp_header_t tcp; + _icmp_header_t icmp; + _icmp_wldr_header_t wldr; + }; +} hicn_v4_hdr_t; + +typedef struct +{ + _ipv4_header_t ip; + union + { + struct + { + _tcp_header_t tcp; + _ah_header_t ah; + }; + struct + { + _icmp_header_t icmp; + _ah_header_t icmp_ah; + }; + }; +} hicn_v4ah_hdr_t; + +typedef union +{ + /* To deprecate as redundant with hicn_type_t */ + hicn_v6_hdr_t v6; + hicn_v6ah_hdr_t v6ah; + hicn_v4_hdr_t v4; + hicn_v4ah_hdr_t v4ah; + + hicn_protocol_t protocol; +} hicn_header_t; + +#endif /* HICN_HEADER_H */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/hicn.h b/lib/src/hicn.h new file mode 100755 index 000000000..749fd4247 --- /dev/null +++ b/lib/src/hicn.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file hicn.h + * @brief hICN master include file. + * + * Reference: https://tools.ietf.org/html/draft-muscariello-intarea-hicn + * + * This file is the entry point for projects to libhicn, which provides a + * reference implementation for hICN specifications [1], including: + * - naming + * - packet headers + * - protocol mappings (IPv4, IPv6, TCP, ICMP, AH) + * - protocol independent packet operations + * - helpers for additional features such as Wireless Loss Detection and + * Recovery (WLDR) [2], Anchorless Mobility Management (hICN-AMM) [3], + * including MAP-Me producer mobility mechanisms [4]. + * + * [1] Hybrid Information-Centric Networking + * L. Muscariello, G. Carofiglio, J. Augé, M. Papalini + * IETF draft (intarea) @ https://tools.ietf.org/html/draft-muscariello-intarea-hicn + * + * [2] Leveraging ICN in-network control for loss detection and recovery in wireless mobile networks + * G. Carofiglio, L. Muscariello, M. Papalini, N. Rozhnova, X. Zeng + * In proc. ICN'2016, Kyoto, JP + * + * [3] Anchorless mobility through hICN + * J. Augé, G. Carofiglio, L. Muscariello, M. Papalini + * IETF draft (DMM) @ https://tools.ietf.org/html/draft-auge-dmm-hicn-mobility + * + * + * [4] MAP-Me : Managing Anchorless Mobility in Content Centric Networking + * J. Augé, G. Carofiglio, L. Muscariello, M. Papalini + * IRTF draft (ICNRG) @ https://tools.ietf.org/html/draft-irtf-icnrg-mapme + */ + +#ifndef HICN__H +#define HICN__H + +#ifdef HICN_VPP_PLUGIN + +#include +#include +#include +#include + +#else + +#include +#include +#include +#include +#include +#include + +#endif + +#endif /* HICN__H */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/mapme.c b/lib/src/mapme.c new file mode 100755 index 000000000..e4c5ee1f2 --- /dev/null +++ b/lib/src/mapme.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file mapme.c + * @brief Implementation of MAP-Me anchorless producer mobility management. + */ + +#include "mapme.h" +#include "common.h" +#include "error.h" + +#include "protocol/ipv4.h" +#include "protocol/ipv6.h" + +size_t +hicn_mapme_v4_create_packet (u8 * buf, const hicn_prefix_t * prefix, + const mapme_params_t * params) +{ + hicn_mapme_v4_header_t *mh = (hicn_mapme_v4_header_t *) buf; + /* *INDENT-OFF* */ + *mh = (hicn_mapme_v4_header_t) { + .ip = { + .version_ihl = (IPV4_DEFAULT_VERSION << 4) | (0x0f & IPV4_DEFAULT_IHL), + .tos = IPV4_DEFAULT_TOS, + .len = HICN_MAPME_V4_HDRLEN, + .id = htons(IPV4_DEFAULT_ID), + .frag_off = htons(IPV4_DEFAULT_FRAG_OFF), + .ttl = HICN_MAPME_TTL, + .protocol = IPPROTO_ICMP, + .csum = 0, + .saddr.as_u32 = 0, + .daddr = prefix->name.ip4, + }, + .icmp = { + .type = ((params->type == UPDATE) || (params->type == NOTIFICATION)) + ? HICN_MAPME_ICMP_TYPE_IPV4 + : HICN_MAPME_ICMP_TYPE_ACK_IPV4, + .code = HICN_MAPME_ICMP_CODE, + .csum = 0, + }, + .icmp_rd = { + .ip = prefix->name.ip4, + }, + .seq = htonl(params->seq), + .len = prefix->len, + }; + /* *INDENT-ON* */ + + return HICN_MAPME_V4_HDRLEN; +} + +size_t +hicn_mapme_v6_create_packet (u8 * buf, const hicn_prefix_t * prefix, + const mapme_params_t * params) +{ + hicn_mapme_v6_header_t *mh = (hicn_mapme_v6_header_t *) buf; + /* *INDENT-OFF* */ + *mh = (hicn_mapme_v6_header_t) { + .ip = { + .saddr = {{0}}, + .daddr = prefix->name.ip6, + .version_class_flow = htonl( + (IPV6_DEFAULT_VERSION << 28) | + (IPV6_DEFAULT_TRAFFIC_CLASS << 20) | + (IPV6_DEFAULT_FLOW_LABEL & 0xfffff)), + .len = htons(HICN_MAPME_V6_HDRLEN - IPV6_HDRLEN), + .nxt = IPPROTO_ICMPV6, + .hlim = HICN_MAPME_TTL, + }, + .icmp = { + .type = ((params->type == UPDATE) || (params->type == NOTIFICATION)) + ? HICN_MAPME_ICMP_TYPE_IPV6 + : HICN_MAPME_ICMP_TYPE_ACK_IPV6, + .code = HICN_MAPME_ICMP_CODE, + .csum = 0, + }, + .icmp_rd = { + .res = 0, + .tgt = prefix->name.ip6, + .dst = prefix->name.ip6, + }, + .seq = htonl(params->seq), + .len = prefix->len, + }; + /* *INDENT-ON* */ + return HICN_MAPME_V6_HDRLEN; +} + +size_t +hicn_mapme_create_packet (u8 * buf, const hicn_prefix_t * prefix, + const mapme_params_t * params) +{ + /* We currently ignore subsequent protocol definitions */ + if (PREDICT_TRUE (params->protocol == IPPROTO_IPV6)) + return hicn_mapme_v6_create_packet (buf, prefix, params); + else + return hicn_mapme_v4_create_packet (buf, prefix, params); +} + +size_t +hicn_mapme_v4_create_ack (u8 * buf, const mapme_params_t * params) +{ + ip4_address_t tmp; // tmp storage for swapping IP addresses for ACK + + hicn_mapme_v4_header_t *mh = (hicn_mapme_v4_header_t *) buf; + tmp = mh->ip.daddr; + mh->ip.daddr = mh->ip.saddr; + mh->ip.saddr = tmp; + mh->ip.ttl = HICN_MAPME_TTL; + mh->icmp.type = (params->type == UPDATE) ? UPDATE_ACK : NOTIFICATION_ACK; + mh->icmp.csum = 0; + + return HICN_MAPME_V4_HDRLEN; +} + +size_t +hicn_mapme_v6_create_ack (u8 * buf, const mapme_params_t * params) +{ + ip6_address_t tmp; // tmp storage for swapping IP addresses for ACK + + hicn_mapme_v6_header_t *mh = (hicn_mapme_v6_header_t *) buf; + tmp = mh->ip.daddr; + mh->ip.daddr = mh->ip.saddr; + mh->ip.saddr = tmp; + mh->ip.hlim = HICN_MAPME_TTL; + mh->icmp.type = (params->type == UPDATE) ? UPDATE_ACK : NOTIFICATION_ACK; + mh->icmp.csum = 0; + + return HICN_MAPME_V6_HDRLEN; +} + +size_t +hicn_mapme_create_ack (u8 * buf, const mapme_params_t * params) +{ + /* We currently ignore subsequent protocol definitions */ + if (PREDICT_TRUE (params->protocol == IPPROTO_IPV6)) + return hicn_mapme_v6_create_ack (buf, params); + else + return hicn_mapme_v4_create_ack (buf, params); +} + +int +hicn_mapme_v4_parse_packet (const u8 * packet, hicn_prefix_t * prefix, + mapme_params_t * params) +{ + hicn_mapme_v4_header_t *mh = (hicn_mapme_v4_header_t *) packet; + + /* *INDENT-OFF* */ + *prefix = (hicn_prefix_t) { + .name = { + .ip4 = HICN_MAPME_TYPE_IS_IU (mh->icmp.type) ? mh->ip.daddr : mh->ip.saddr, + }, + .len = mh->len, + }; + + *params = (mapme_params_t) { + .protocol = IPPROTO_IP, + .type = (mh->icmp.type == HICN_MAPME_ICMP_TYPE_IPV4) ? UPDATE : UPDATE_ACK, + .seq = ntohl (mh->seq), + }; + /* *INDENT-ON* */ + + return HICN_LIB_ERROR_NONE; +} + +int +hicn_mapme_v6_parse_packet (const u8 * packet, hicn_prefix_t * prefix, + mapme_params_t * params) +{ + hicn_mapme_v6_header_t *mh = (hicn_mapme_v6_header_t *) packet; + + /* *INDENT-OFF* */ + *prefix = (hicn_prefix_t) { + .name = { + .ip6 = HICN_MAPME_TYPE_IS_IU (mh->icmp.type) ? mh->ip.daddr : mh->ip.saddr, + }, + .len = mh->len, + }; + + *params = (mapme_params_t) { + .protocol = IPPROTO_IPV6, + .type = (mh->icmp.type == HICN_MAPME_ICMP_TYPE_IPV6) ? UPDATE : UPDATE_ACK, + .seq = ntohl (mh->seq), + }; + /* *INDENT-ON* */ + + return HICN_LIB_ERROR_NONE; +} + +int +hicn_mapme_parse_packet (const u8 * packet, hicn_prefix_t * prefix, + mapme_params_t * params) +{ + switch (HICN_IP_VERSION (packet)) + { + case 4: + return hicn_mapme_v4_parse_packet (packet, prefix, params); + case 6: + return hicn_mapme_v6_parse_packet (packet, prefix, params); + default: + return HICN_LIB_ERROR_UNEXPECTED; + } +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/mapme.h b/lib/src/mapme.h new file mode 100755 index 000000000..460c15282 --- /dev/null +++ b/lib/src/mapme.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file mapme.h + * @brief MAP-Me anchorless producer mobility management. + */ +#ifndef HICN_MAPME_H +#define HICN_MAPME_H + +#include // u32 +#include + +#include "common.h" +#include "protocol.h" +#include "ops.h" + +/** + * @brief MAP-Me configuration options + */ +typedef struct +{ + /** MAP-Me enabled flag (default: false) */ + bool enabled; + /** timescale (T_U parameter) in ms (default: 0 for no notifications) */ + u32 timescale; + /** retransmission timer in ms (default: 50) */ + u32 retx; + /** + * Discovery enabled flag (default: true, should be true if mandatory is + * notifications are enabled) + */ + bool discovery; +} hicn_mapme_conf_t; + +/** @brief Default MAP-Me configuration */ +static const hicn_mapme_conf_t hicn_mapme_conf = { + .enabled = false, + .timescale = 0, + .retx = 50, + .discovery = true, +}; + +/** @brief MAP-Me update sequence number */ +typedef u32 seq_t; + +/** @brief MAP-Me packet types */ +typedef enum +{ + UNKNOWN, + UPDATE, + UPDATE_ACK, + NOTIFICATION, + NOTIFICATION_ACK, +} hicn_mapme_type_t; + +/** @brief MAP-Me parameters (excluding those contained in * hicn_prefix_t) */ +typedef struct +{ + int protocol; + hicn_mapme_type_t type; + seq_t seq; +} mapme_params_t; + + +/* MAP-Me API */ +size_t hicn_mapme_create_packet (u8 * buf, const hicn_prefix_t * prefix, + const mapme_params_t * params); +size_t hicn_mapme_create_ack (u8 * buf, const mapme_params_t * params); +int hicn_mapme_parse_packet (const u8 * packet, hicn_prefix_t * prefix, + mapme_params_t * params); + +/* Implementation & parsing : ICMP Redirect */ + +#define HEADER_TYPE_MAPME4 (hicn_type_t) {0, IPPROTO_ICMPRD, IPPROTO_ICMP, IPPROTO_IP} +#define HEADER_TYPE_MAPME6 (hicn_type_t) {0, IPPROTO_ICMPRD, IPPROTO_ICMP, IPPROTO_IPV6} + +#define HICN_MAPME_ACK_FLAG (0x20 | 0x60) + +#define HICN_MAPME_ICMP_TYPE_IPV4 5 +#define HICN_MAPME_ICMP_TYPE_IPV6 137 +#define HICN_MAPME_ICMP_TYPE_ACK_IPV4 (HICN_MAPME_ICMP_TYPE_IPV4 | HICN_MAPME_ACK_FLAG) +#define HICN_MAPME_ICMP_TYPE_ACK_IPV6 (HICN_MAPME_ICMP_TYPE_IPV6 | HICN_MAPME_ACK_FLAG) +#define HICN_MAPME_ICMP_CODE 0 /* Redirect Datagrams for the Network (or subnet) */ + +#define HICN_MAPME_TYPE_IS_IU(type) ((type == HICN_MAPME_ICMP_TYPE_IPV4) || (type == HICN_MAPME_ICMP_TYPE_IPV6)) +#define HICN_MAPME_TYPE_IS_IU_ACK(type) ((type == HICN_MAPME_ICMP_TYPE_ACK_IPV4) || (type == HICN_MAPME_ICMP_TYPE_ACK_IPV6)) + +#define HICN_MAPME_IS_IU(type, code) (HICN_MAPME_TYPE_IS_IU(type) && (code == HICN_MAPME_ICMP_CODE)) +#define HICN_MAPME_IS_ACK(type, code) (HICN_MAPME_TYPE_IS_IU_ACK(type) && (code == HICN_MAPME_ICMP_CODE)) + +#define HICN_IS_MAPME(type, code) (HICN_MAPME_IS_IU(type, code) || HICN_MAPME_IS_ACK(type, code)) + +/* Fast check for ACK flag */ +#define HICN_MAPME_IS_ACK_FAST(icmp_type) (icmp_type & HICN_MAPME_ACK_FLAG) + +/* Default TTL */ +#define HICN_MAPME_TTL 255 // typical for redirect (ref?) + +/** @brief MAP-Me packet header for IPv4 */ +typedef struct __attribute__ ((packed)) +{ + _ipv4_header_t ip; + _icmp_header_t icmp; + _icmprd4_header_t icmp_rd; + seq_t seq; + u8 len; + u8 _pad[3]; +} hicn_mapme_v4_header_t; + +/** @brief MAP-Me packet header for IPv6 */ +typedef struct __attribute__ ((packed)) +{ + _ipv6_header_t ip; + _icmp_header_t icmp; + _icmprd_header_t icmp_rd; + seq_t seq; + u8 len; + u8 _pad[3]; +} hicn_mapme_v6_header_t; + +/** @brief MAP-Me packet header (IP version agnostic) */ +typedef union +{ + hicn_mapme_v4_header_t v4; + hicn_mapme_v6_header_t v6; +} hicn_mapme_header_t; + +#define HICN_MAPME_V4_HDRLEN sizeof(hicn_mapme_v4_header_t) +#define HICN_MAPME_V6_HDRLEN sizeof(hicn_mapme_v6_header_t) + +#endif /* HICN_MAPME_H */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/name.c b/lib/src/name.c new file mode 100755 index 000000000..6e5711252 --- /dev/null +++ b/lib/src/name.c @@ -0,0 +1,676 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file name.c + * @brief Implementation of hICN name helpers. + */ + +#include // inet_ptin +#include +#include +#include // strtoul +#include // memcpy + +#include "common.h" +#include "error.h" +#include "name.h" + +#define DUMMY_PORT ntohs(1234) + +#if ! HICN_VPP_PLUGIN +int +hicn_name_create (const char *ip_address, u32 id, hicn_name_t * name) +{ + int af, rc; + + af = get_addr_family (ip_address); + + switch (af) + { + case AF_INET: + if (name->type == HNT_UNSPEC) + { + name->type = HNT_CONTIGUOUS_V4; + } + name->len = IPV4_ADDR_LEN; + break; + case AF_INET6: + if (name->type == HNT_UNSPEC) + { + name->type = HNT_CONTIGUOUS_V6; + } + name->len = IPV6_ADDR_LEN; + break; + default: + return HICN_LIB_ERROR_INVALID_IP_ADDRESS; + } + + if ((name->type != HNT_CONTIGUOUS_V4) && (name->type != HNT_CONTIGUOUS_V6)) + { + return HICN_LIB_ERROR_NOT_IMPLEMENTED; + } + + rc = inet_pton (af, ip_address, name->buffer); + if (rc <= 0) + { + return HICN_LIB_ERROR_UNKNOWN_ADDRESS; + } + *(u32 *) (name->buffer + name->len) = id; + + return HICN_LIB_ERROR_NONE; +} + +int +hicn_name_create_from_ip_address (const ip_address_t * ip_address, u32 id, + hicn_name_t * name) +{ + switch (ip_address->family) + { + case AF_INET: + if (name->type == HNT_UNSPEC) + { + name->type = HNT_CONTIGUOUS_V4; + } + break; + case AF_INET6: + if (name->type == HNT_UNSPEC) + { + name->type = HNT_CONTIGUOUS_V6; + } + break; + default: + return HICN_LIB_ERROR_INVALID_IP_ADDRESS; + } + + name->len = ip_address->prefix_len; + if ((name->type != HNT_CONTIGUOUS_V4) && (name->type != HNT_CONTIGUOUS_V6)) + { + return HICN_LIB_ERROR_NOT_IMPLEMENTED; + } + + memcpy (name->buffer, ip_address->buffer, ip_address_len (ip_address)); + *(u32 *) (name->buffer + name->len) = id; + + return HICN_LIB_ERROR_NONE; +} + +u8 +hicn_name_get_length (const hicn_name_t * name) +{ + return name->len; +} + +int +hicn_name_compare (const hicn_name_t * name_1, const hicn_name_t * name_2, + bool consider_segment) +{ + hicn_name_t *name1 = (hicn_name_t *) name_1; + hicn_name_t *name2 = (hicn_name_t *) name_2; + + if ((name1->type == HNT_CONTIGUOUS_V4 && name2->type == HNT_CONTIGUOUS_V6) + || (name1->type == HNT_CONTIGUOUS_V6 + && name2->type == HNT_CONTIGUOUS_V4)) + { + return -1; + } + + if ((name1->type == HNT_IOV_V4 && name2->type == HNT_IOV_V6) || + (name1->type == HNT_IOV_V6 && name2->type == HNT_IOV_V4)) + { + return -1; + } + + if ((name1->type == HNT_IOV_V4 && name2->type == HNT_CONTIGUOUS_V6) || + (name1->type == HNT_IOV_V6 && name2->type == HNT_CONTIGUOUS_V4)) + { + return -1; + } + + if (name1->type == HNT_UNSPEC || name2->type == HNT_UNSPEC) + { + return -1; + } + + size_t len1 = 0, len2 = 0; + + u8 *buffer11, *buffer12, *buffer21, *buffer22; + + switch (name1->type) + { + case HNT_CONTIGUOUS_V4: + buffer11 = name1->buffer; + buffer12 = name1->buffer + IPV4_ADDR_LEN; + len1 = IPV4_ADDR_LEN; + break; + case HNT_CONTIGUOUS_V6: + buffer11 = name1->buffer; + buffer12 = name1->buffer + IPV6_ADDR_LEN; + len1 = IPV6_ADDR_LEN; + break; + case HNT_IOV_V4: + buffer11 = name1->iov.buffers[0].iov_base; + buffer12 = name1->iov.buffers[1].iov_base; + len1 = IPV4_ADDR_LEN; + break; + case HNT_IOV_V6: + buffer11 = name1->iov.buffers[0].iov_base; + buffer12 = name1->iov.buffers[1].iov_base; + len1 = IPV6_ADDR_LEN; + break; + default: + return HICN_LIB_ERROR_NOT_IMPLEMENTED; + } + + switch (name2->type) + { + case HNT_CONTIGUOUS_V4: + buffer21 = name2->buffer; + buffer22 = name2->buffer + IPV4_ADDR_LEN; + len2 = IPV4_ADDR_LEN; + break; + case HNT_CONTIGUOUS_V6: + buffer21 = name2->buffer; + buffer22 = name2->buffer + IPV6_ADDR_LEN; + len2 = IPV6_ADDR_LEN; + break; + case HNT_IOV_V4: + buffer21 = name2->iov.buffers[0].iov_base; + buffer22 = name2->iov.buffers[1].iov_base; + len2 = IPV4_ADDR_LEN; + break; + case HNT_IOV_V6: + buffer21 = name2->iov.buffers[0].iov_base; + buffer22 = name2->iov.buffers[1].iov_base; + len2 = IPV6_ADDR_LEN; + break; + default: + return HICN_LIB_ERROR_NOT_IMPLEMENTED; + } + + // Sanity check + if (len1 != len2) + { + return HICN_LIB_ERROR_UNEXPECTED; + } + + int ret1 = memcmp ((u8 *) buffer11, (u8 *) buffer21, len1); + + if (!consider_segment) + { + return ret1; + } + + int ret2 = memcmp ((u8 *) buffer12, (u8 *) buffer22, HICN_SEGMENT_LEN); + + return ret1 || ret2; +} + +int +hicn_name_hash (const hicn_name_t * name, u32 * hash) +{ + switch (name->type) + { + case HNT_CONTIGUOUS_V4: + *hash = hash32 (name->buffer, HICN_V4_NAME_LEN); + break; + case HNT_CONTIGUOUS_V6: + *hash = hash32 (name->buffer, HICN_V6_NAME_LEN); + break; + case HNT_IOV_V4: + case HNT_IOV_V6: + *hash = + hash32 (name->iov.buffers[0].iov_base, name->iov.buffers[0].iov_len); + *hash = + cumulative_hash32 (name->iov.buffers[1].iov_base, + name->iov.buffers[1].iov_len, *hash); + break; + default: + return HICN_LIB_ERROR_NOT_IMPLEMENTED; + } + + return HICN_LIB_ERROR_NONE; +} + +int +hicn_name_empty (hicn_name_t * name) +{ + return name->type == HNT_UNSPEC ? HICN_LIB_ERROR_NONE : 1; +} + +int +hicn_name_copy (hicn_name_t * dst, const hicn_name_t * src) +{ + switch (src->type) + { + case HNT_CONTIGUOUS_V4: + case HNT_CONTIGUOUS_V6: + *dst = *src; + break; + case HNT_IOV_V4: + case HNT_IOV_V6: + dst->type = + src->type == HNT_IOV_V4 ? HNT_CONTIGUOUS_V4 : HNT_CONTIGUOUS_V6; + memcpy (dst->buffer, src->iov.buffers[0].iov_base, + src->iov.buffers[0].iov_len); + memcpy (dst->buffer + src->iov.buffers[0].iov_len, + src->iov.buffers[1].iov_base, src->iov.buffers[1].iov_len); + break; + default: + return HICN_LIB_ERROR_NOT_IMPLEMENTED; + } + + return HICN_LIB_ERROR_NONE; +} + +int +hicn_name_copy_to_destination (u8 * dst, const hicn_name_t * src, + bool copy_suffix) +{ + size_t length; + + switch (src->type) + { + case HNT_CONTIGUOUS_V4: + if (copy_suffix) + { + length = HICN_V4_NAME_LEN; + } + else + { + length = IPV4_ADDR_LEN; + } + memcpy (dst, src->buffer, length); + break; + case HNT_CONTIGUOUS_V6: + if (copy_suffix) + { + length = HICN_V6_NAME_LEN; + } + else + { + length = IPV6_ADDR_LEN; + } + memcpy (dst, src->buffer, length); + break; + case HNT_IOV_V4: + case HNT_IOV_V6: + memcpy (dst, src->iov.buffers[0].iov_base, src->iov.buffers[0].iov_len); + if (copy_suffix) + { + memcpy (dst + src->iov.buffers[0].iov_len, + src->iov.buffers[1].iov_base, src->iov.buffers[1].iov_len); + } + break; + default: + return HICN_LIB_ERROR_NOT_IMPLEMENTED; + } + + return HICN_LIB_ERROR_NONE; +} + +int +hicn_name_set_seq_number (hicn_name_t * name, u32 seq_number) +{ + u8 *sequence_number = NULL; + + switch (name->type) + { + case HNT_CONTIGUOUS_V6: + sequence_number = name->buffer + IPV6_ADDR_LEN; + break; + case HNT_CONTIGUOUS_V4: + sequence_number = name->buffer + IPV4_ADDR_LEN; + break; + case HNT_IOV_V6: + case HNT_IOV_V4: + sequence_number = name->iov.buffers[1].iov_base; + break; + case HNT_UNSPEC: + return HICN_LIB_ERROR_UNEXPECTED; + } + + if (sequence_number) + { + *(u32 *) sequence_number = seq_number; + } + else + { + return HICN_LIB_ERROR_UNEXPECTED; + } + + return HICN_LIB_ERROR_NONE; +} + +int +hicn_name_to_sockaddr_address (const hicn_name_t * name, + struct sockaddr *ip_address) +{ + struct sockaddr_in6 *tmp6 = (struct sockaddr_in6 *) ip_address; + struct sockaddr_in *tmp4 = (struct sockaddr_in *) ip_address; + + switch (name->type) + { + case HNT_CONTIGUOUS_V6: + tmp6->sin6_family = AF_INET6; + tmp6->sin6_scope_id = 0; + tmp6->sin6_port = DUMMY_PORT; + memcpy (&tmp6->sin6_addr, name->buffer, IPV6_ADDR_LEN); + break; + case HNT_IOV_V6: + tmp6->sin6_family = AF_INET6; + tmp6->sin6_scope_id = 0; + tmp6->sin6_port = DUMMY_PORT; + memcpy (&tmp6->sin6_addr, name->iov.buffers[0].iov_base, + name->iov.buffers[0].iov_len); + break; + case HNT_CONTIGUOUS_V4: + tmp4->sin_family = AF_INET; + tmp4->sin_port = DUMMY_PORT; + memcpy (&tmp4->sin_addr, name->buffer, IPV4_ADDR_LEN); + break; + case HNT_IOV_V4: + tmp4->sin_family = AF_INET; + tmp4->sin_port = DUMMY_PORT; + memcpy (&tmp4->sin_addr, name->iov.buffers[0].iov_base, + name->iov.buffers[0].iov_len); + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + + return HICN_LIB_ERROR_NONE; +} + +int +hicn_name_to_ip_address (const hicn_name_t * name, ip_address_t * ip_address) +{ + switch (name->type) + { + case HNT_CONTIGUOUS_V6: + memcpy (&ip_address->buffer, name->buffer, IPV6_ADDR_LEN); + ip_address->family = AF_INET6; + break; + case HNT_IOV_V6: + memcpy (&ip_address->buffer, name->iov.buffers[0].iov_base, + name->iov.buffers[0].iov_len); + ip_address->family = AF_INET6; + break; + case HNT_CONTIGUOUS_V4: + memcpy (&ip_address->buffer, name->buffer, IPV4_ADDR_LEN); + ip_address->family = AF_INET; + break; + case HNT_IOV_V4: + memcpy (&ip_address->buffer, name->iov.buffers[0].iov_base, + name->iov.buffers[0].iov_len); + ip_address->family = AF_INET; + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + + return HICN_LIB_ERROR_NONE; +} + +int +hicn_name_get_seq_number (const hicn_name_t * name, u32 * seq_number) +{ + const u8 *sequence_number = NULL; + + switch (name->type) + { + case HNT_CONTIGUOUS_V6: + sequence_number = name->buffer + IPV6_ADDR_LEN; + break; + case HNT_CONTIGUOUS_V4: + sequence_number = name->buffer + IPV4_ADDR_LEN; + break; + case HNT_IOV_V6: + case HNT_IOV_V4: + sequence_number = name->iov.buffers[1].iov_base; + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + + if (sequence_number) + { + *seq_number = *(u32 *) sequence_number; + } + else + { + return HICN_LIB_ERROR_UNEXPECTED; + } + + return HICN_LIB_ERROR_NONE; +} + +int +hicn_name_ntop (const hicn_name_t * src, char *dst, size_t len) +{ + int offset; + const char *rc; + void *seg_number = NULL; + + switch (src->type) + { + case HNT_CONTIGUOUS_V6: + rc = inet_ntop (AF_INET6, src->buffer, dst, len); + seg_number = (u8 *) src->buffer + IPV6_ADDR_LEN; + break; + case HNT_CONTIGUOUS_V4: + rc = inet_ntop (AF_INET, src->buffer, dst, len); + seg_number = (u8 *) src->buffer + IPV4_ADDR_LEN; + break; + case HNT_IOV_V6: + rc = inet_ntop (AF_INET6, src->iov.buffers[0].iov_base, dst, len); + seg_number = src->iov.buffers[1].iov_base; + break; + case HNT_IOV_V4: + rc = inet_ntop (AF_INET, src->iov.buffers[0].iov_base, dst, len); + seg_number = src->iov.buffers[1].iov_base; + break; + default: + return HICN_LIB_ERROR_NOT_IMPLEMENTED; + } + + if (!rc) + { + goto ERR; + } + + offset = strlen (dst); + dst[offset] = '|'; + + sprintf (dst + offset + 1, "%lu", (unsigned long) (*(u32 *) seg_number)); + + return HICN_LIB_ERROR_NONE; + +ERR: + return HICN_LIB_ERROR_UNSPECIFIED; +} + +int +hicn_name_pton (const char *src, hicn_name_t * dst) +{ + return HICN_LIB_ERROR_NOT_IMPLEMENTED; +} + +int +hicn_name_get_family (const hicn_name_t * name, int *family) +{ + switch (name->type) + { + case HNT_CONTIGUOUS_V6: + case HNT_IOV_V6: + *family = AF_INET6; + break; + case HNT_CONTIGUOUS_V4: + case HNT_IOV_V4: + *family = AF_INET; + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + + return HICN_LIB_ERROR_NONE; +} + +int +hicn_prefix_create_from_ip_address (const ip_address_t * ip_address, + hicn_prefix_t * prefix) +{ + switch (ip_address->family) + { + case AF_INET: + prefix->name.ip4.as_u32 = ip_address->as_u32[0]; + break; + case AF_INET6: + prefix->name.ip6.as_u64[0] = ip_address->as_u64[0]; + prefix->name.ip6.as_u64[1] = ip_address->as_u64[1]; + break; + default: + return HICN_LIB_ERROR_INVALID_IP_ADDRESS; + } + prefix->len = ip_address->prefix_len; + + return HICN_LIB_ERROR_NONE; +} + +#endif /* ! HICN_VPP_PLUGIN */ + +/******** + * IP + */ + +inline int +ip_address_len (const ip_address_t * ip_address) +{ + return (ip_address->family == AF_INET6) ? IPV6_ADDR_LEN : + (ip_address->family == AF_INET) ? IPV4_ADDR_LEN : 0; +} + +bool +ip_address_empty (const ip_address_t * ip_address) +{ + return ip_address->prefix_len == 0; +} + +int +hicn_ip_ntop (const ip_address_t * ip_address, char *dst, const size_t len) +{ + const char *rc; + + rc = inet_ntop (ip_address->family, ip_address->buffer, dst, len); + if (!rc) + { + printf ("error ntop: %d %s\n", errno, strerror (errno)); + return HICN_LIB_ERROR_INVALID_IP_ADDRESS; + } + + return HICN_LIB_ERROR_NONE; +} + +/* + * Parse ip addresses in presentation format, or prefixes (in bits, separated by a slash) + */ +int +hicn_ip_pton (const char *ip_address_str, ip_address_t * ip_address) +{ + int pton_fd; + char *p; + char *eptr; + u32 dst_len; + char *addr = strdup (ip_address_str); + + p = strchr (addr, '/'); + if (!p) + { + dst_len = 0; // until we get the ip address family + } + else + { + dst_len = strtoul (p + 1, &eptr, 10); + *p = 0; + } + + ip_address->family = get_addr_family (addr); + + switch (ip_address->family) + { + case AF_INET6: + if (dst_len > IPV6_ADDR_LEN_BITS) + goto ERR; + pton_fd = inet_pton (AF_INET6, addr, &ip_address->buffer); + ip_address->prefix_len = dst_len ? : IPV6_ADDR_LEN_BITS; + break; + case AF_INET: + if (dst_len > IPV4_ADDR_LEN_BITS) + goto ERR; + pton_fd = inet_pton (AF_INET, addr, &ip_address->buffer); + ip_address->prefix_len = dst_len ? : IPV4_ADDR_LEN_BITS; + break; + default: + goto ERR; + } + + // 0 = not in presentation format + // < 0 = other error (use perror) + if (pton_fd <= 0) + { + goto ERR; + } + + return HICN_LIB_ERROR_NONE; +ERR: + free (addr); + return HICN_LIB_ERROR_INVALID_IP_ADDRESS; +} + +int +hicn_ip_to_sockaddr_address (const ip_address_t * ip_address, + struct sockaddr *sockaddr_address) +{ + struct sockaddr_in6 *tmp6 = (struct sockaddr_in6 *) sockaddr_address; + struct sockaddr_in *tmp4 = (struct sockaddr_in *) sockaddr_address; + + switch (ip_address->family) + { + case AF_INET6: + tmp6->sin6_family = AF_INET6; + tmp6->sin6_port = DUMMY_PORT; + tmp6->sin6_scope_id = 0; + memcpy (&tmp6->sin6_addr, ip_address->buffer, IPV6_ADDR_LEN); + break; + case AF_INET: + tmp4->sin_family = AF_INET; + tmp4->sin_port = DUMMY_PORT; + memcpy (&tmp4->sin_addr, ip_address->buffer, IPV4_ADDR_LEN); + break; + default: + return HICN_LIB_ERROR_UNEXPECTED; + } + + return HICN_LIB_ERROR_NONE; +} + + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/name.h b/lib/src/name.h new file mode 100755 index 000000000..0a55fedc6 --- /dev/null +++ b/lib/src/name.h @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file name.h + * @brief hICN name helpers. + * + * The purpose of the file is to offer an efficient, platform- and protocol- + * independent way to manipulate hICN names. + */ + +#ifndef HICN_NAME_H +#define HICN_NAME_H + +#include +#include // struct sockadd + +#include "common.h" + +/****************************************************************************** + * IP address helpers + ******************************************************************************/ + +/* Presentation format */ +#define INET_ADDRSTRLEN 16 +#define INET6_ADDRSTRLEN 46 +//#define INET_MAX_ADDRSTRLEN INET6_ADDRSTRLEN + +/* Address size */ +#define bytes_to_bits(x) (x * 8) +#define IPV6_ADDR_LEN 16 /* bytes */ +#define IPV4_ADDR_LEN 4 /* bytes */ +#define IPV6_ADDR_LEN_BITS bytes_to_bits(IPV6_ADDR_LEN) +#define IPV4_ADDR_LEN_BITS bytes_to_bits(IPV4_ADDR_LEN) + +#define IP_MAX_ADDR_LEN IPV6_ADDR_LEN +#define TCP_SEQNO_LEN 4 /* bytes */ + +typedef struct +{ + union + { + u8 buffer[IP_MAX_ADDR_LEN]; + u8 as_u8[IP_MAX_ADDR_LEN]; + u16 as_u16[IP_MAX_ADDR_LEN >> 1]; + u32 as_u32[IP_MAX_ADDR_LEN >> 2]; + u64 as_u64[IP_MAX_ADDR_LEN >> 3]; + ip4_address_t as_ip4; + ip6_address_t as_ip6; + }; + int family; + unsigned short prefix_len; +} ip_address_t; + +int ip_address_len (const ip_address_t * ip_address); +bool ip_address_empty (const ip_address_t * ip_address); + +int hicn_ip_ntop (const ip_address_t * ip_address, char *dst, + const size_t len); +int hicn_ip_pton (const char *ip_address_str, ip_address_t * ip_address); +int hicn_ip_to_sockaddr_address (const ip_address_t * ip_address, + struct sockaddr *sockaddr_address); + +/****************************************************************************** + * hICN names + ******************************************************************************/ + +#define HICN_V4_PREFIX_LEN IPV4_ADDR_LEN +#define HICN_V6_PREFIX_LEN IPV6_ADDR_LEN +#define HICN_SEGMENT_LEN TCP_SEQNO_LEN +#define HICN_V6_NAME_LEN (HICN_V6_PREFIX_LEN + HICN_SEGMENT_LEN) /* 20 bytes */ +#define HICN_V4_NAME_LEN (HICN_V4_PREFIX_LEN + HICN_SEGMENT_LEN) /* 8 bytes */ + +/* Prefix */ + +typedef u32 hicn_name_suffix_t; + +typedef struct +{ + ip46_address_t name; + u8 len; +} hicn_prefix_t; + +/* + * Name + * + * A name is a prefix + a segment name (suffix) + */ + +typedef union +{ + struct + { + union + { + u32 prefix; + u8 prefix_as_u8[4]; + ip4_address_t prefix_as_ip4; + }; + hicn_name_suffix_t suffix; + }; + u8 buffer[HICN_V4_NAME_LEN]; +} hicn_v4_name_t; + +typedef union +{ + struct + { + union + { + u64 prefix[2]; + u8 prefix_as_u8[16]; + ip6_address_t prefix_as_ip6; + }; + hicn_name_suffix_t suffix; + }; + u8 buffer[HICN_V6_NAME_LEN]; +} hicn_v6_name_t; + +typedef struct +{ + u8 buffer[0]; +} hicn_v46_name_t; + +#ifndef HICN_VPP_PLUGIN +#define HICN_NAME_COMPONENT_SIZE 2 + +typedef struct +{ + struct iovec buffers[HICN_NAME_COMPONENT_SIZE]; +} hicn_iov_name_t; + +#define UNSPEC 1 << 0 +#define HNT_CONTIGUOUS 1 << 1 +#define HNT_IOV 1 << 2 +#define HNT_INET 1 << 3 +#define HNT_INET6 1 << 4 + +typedef enum +{ + HNT_UNSPEC = UNSPEC, + HNT_CONTIGUOUS_V4 = HNT_CONTIGUOUS | HNT_INET, + HNT_CONTIGUOUS_V6 = HNT_CONTIGUOUS | HNT_INET6, + HNT_IOV_V4 = HNT_IOV | HNT_INET, + HNT_IOV_V6 = HNT_IOV | HNT_INET6, +} hicn_name_type_t; +#endif /* HICN_VPP_PLUGIN */ + +typedef struct +{ +#ifndef HICN_VPP_PLUGIN + hicn_name_type_t type; + u8 len; +#endif /* HICN_VPP_PLUGIN */ + union + { + hicn_v4_name_t ip4; + hicn_v6_name_t ip6; + ip46_address_t ip46; +#ifndef HICN_VPP_PLUGIN + hicn_iov_name_t iov; + u8 buffer[0]; +#endif /* HICN_VPP_PLUGIN */ + }; +} hicn_name_t; + +#ifndef HICN_VPP_PLUGIN +#define _is_unspec(name) ((name->type & UNSPEC)) +#define _is_contiguous(name) ((name->type & HNT_CONTIGUOUS) >> 1) +#define _is_iov(name) ((name->type & HNT_IOV) >> 2) +#define _is_inet4(name) ((name->type & HNT_INET) >> 3) +#define _is_inet6(name) ((name->type & HNT_INET6) >> 4) +#endif /* HICN_VPP_PLUGIN */ + +/** + * @brief Create an hICN name from IP address in presentation format + * @param [in] ip_address - IP address + * @param [in] id - Segment identifier + * @param [out] Resulting hICN name + * @return hICN error code + */ +int hicn_name_create (const char *ip_address, u32 id, hicn_name_t * name); + +/** + * @brief Create an hICN name from IP address + * @param [in] ip_address - IP address + * @param [in] id Segment - identifier + * @param [out] Resulting - hICN name + * @return hICN error code + */ +int hicn_name_create_from_ip_address (const ip_address_t * ip_address, u32 id, + hicn_name_t * name); + +/** + * @brief Returns the length of an hICN name + * @param [in] name - hICN name + * @return Name length + */ +u8 hicn_name_get_length (const hicn_name_t * name); + +/** + * @brief Compare two hICN names + * @param [in] name_1 - First name to compare + * @param [in] name_2 - Second name to compare + * @param [in] consider_segment - Flag indicating whether the segment part has to be + * considered + * @return An integer less than, equal to, or greater than zero if name_1 is + * found, respectively, to be lest than, to match, or be greater than name_2 + * based on numeric order. + */ +int hicn_name_compare (const hicn_name_t * name_1, const hicn_name_t * name_2, + bool consider_segment); + +/** + * @brief Provides a 32-bit hash of an hICN name + * @param [in] name - Name to hash + * @param [out] hash - Resulting hash + * @return hICN error code + */ +int hicn_name_hash (const hicn_name_t * name, u32 * hash); + +/** + * @brief Test whether an hICN name is empty + * @param [in] name - Name to test + * @return 0 if the name is empty, any other value otherwise (implementation + * returns 1) + */ +int hicn_name_empty (hicn_name_t * name); + +/** + * @brief Copy an hICN name + * @param [out] dst - Destination name + * @param [in] src - Source name to copy + * @return hICN error code + */ +int hicn_name_copy (hicn_name_t * dst, const hicn_name_t * src); + +/** + * @brief Copy an hICN name to a buffer + * @param [out] dst - Destination buffer + * @param [in] src - Source name to copy + * @param [in] copy_suffix - Flag indicating whether the suffix has to be + * considered + */ +int hicn_name_copy_to_destination (u8 * dst, const hicn_name_t * src, + bool copy_suffix); + +/** + * @brief Sets the segment part of an hICN name + * @param [in,out] name - hICN name to modify + * @param [in] seq_number - Segment identifier + * @return hICN error code + */ +int hicn_name_set_seq_number (hicn_name_t * name, u32 seq_number); + +/** + * @brief Retrieves the segment part of an hICN name + * @param [in,out] name - hICN name + * @param [in] seq_number - Segment identifier + * @return hICN error code + */ +int hicn_name_get_seq_number (const hicn_name_t * name, u32 * seq_number); + +/** + * @brief Convert an hICN name to a socket address + * @param [in] name - Name to convert + * @param [out] ip_address - Resulting socket address + * @return hICN error code + */ +int hicn_name_to_sockaddr_address (const hicn_name_t * name, + struct sockaddr *ip_address); + +/** + * @brief Convert an hICN name to an IP address + * @param [in] name - Name to convert + * @param [out] ip_address - Resulting IP address + * @return hICN error code + */ +int hicn_name_to_ip_address (const hicn_name_t * name, + ip_address_t * ip_address); + +/** + * @brief Convert an hICN name to presentation format + * @param [in] src - Name to convert + * @param [out] dst - Buffer to receive the name in presentation format + * @param [in] len - Number of bytes available in the buffer + * @return hICN error code + */ +int hicn_name_ntop (const hicn_name_t * src, char *dst, size_t len); + +/** + * @brief Convert an hICN name from presentation format + * @param [in] src - Name in presentation format to parse + * @param [out] dst - Resulting name + * @return hICN error code + */ +int hicn_name_pton (const char *src, hicn_name_t * dst); + +/** + * @brief Returns the IP address family of an hICN name + * @param [in] name - Name to lookup + * @param [out] family - Resulting IP address family (AF_INET or AF_INET6) + * @return hICN error code + */ +int hicn_name_get_family (const hicn_name_t * name, int *family); + +/** + * @brief Creates an hICN prefix from an IP address + * @param [in] ip_address - Input IP address + * @param [out] prefix - Resulting prefix + * @return hICN error code + */ +int hicn_prefix_create_from_ip_address (const ip_address_t * ip_address, + hicn_prefix_t * prefix); + +#endif /* HICN_NAME_H */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/ops.c b/lib/src/ops.c new file mode 100755 index 000000000..ad45a13a5 --- /dev/null +++ b/lib/src/ops.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ops.c + * @brief Initializers for protocol-independent packet operations + */ + +#include +#include +#include "ops.h" + +#include "header.h" + +extern const hicn_ops_t hicn_ops_ipv4; +extern const hicn_ops_t hicn_ops_icmp; +extern const hicn_ops_t hicn_ops_tcp; +extern const hicn_ops_t hicn_ops_ipv6; +extern const hicn_ops_t hicn_ops_ah; + +/* Declare empty operations (terminates recursion on protocol layers) */ +DECLARE_init_packet_header (none, NONE); +DECLARE_get_interest_locator (none, NONE); +DECLARE_set_interest_locator (none, NONE); +DECLARE_get_interest_name (none, NONE); +DECLARE_set_interest_name (none, NONE); +DECLARE_get_interest_name_suffix (none, NONE); +DECLARE_set_interest_name_suffix (none, NONE); +DECLARE_reset_interest_for_hash (none, NONE); +DECLARE_get_data_locator (none, NONE); +DECLARE_set_data_locator (none, NONE); +DECLARE_get_data_name (none, NONE); +DECLARE_set_data_name (none, NONE); +DECLARE_get_data_name_suffix (none, NONE); +DECLARE_set_data_name_suffix (none, NONE); +DECLARE_get_data_pathlabel (none, NONE); +DECLARE_set_data_pathlabel (none, NONE); +DECLARE_update_data_pathlabel (none, NONE); +DECLARE_reset_data_for_hash (none, NONE); +DECLARE_get_lifetime (none, NONE); +DECLARE_set_lifetime (none, NONE); +DECLARE_update_checksums (none, NONE); +DECLARE_verify_checksums (none, NONE); +DECLARE_rewrite_interest (none, NONE); +DECLARE_rewrite_data (none, NONE); +DECLARE_get_length (none, NONE); +DECLARE_get_header_length (none, NONE); +DECLARE_get_current_header_length (none, NONE); +DECLARE_get_payload_length (none, NONE); +DECLARE_set_payload_length (none, NONE); +DECLARE_get_signature_size (none, NONE); +DECLARE_set_signature_size (none, NONE); +DECLARE_set_signature_timestamp (none, NONE); +DECLARE_get_signature_timestamp (none, NONE); +DECLARE_set_validation_algorithm (none, NONE); +DECLARE_get_validation_algorithm (none, NONE); +DECLARE_set_key_id (none, NONE); +DECLARE_get_key_id (none, NONE); +DECLARE_HICN_OPS (none); + +/** + * @brief Virtual function table for packet operations + * NOTE: protocol numbers have to be kept in order + */ +const hicn_ops_t *const hicn_ops_vft[] = { + /* 0 */ [IPPROTO_IP] = &hicn_ops_ipv4, + /* 1 */ [IPPROTO_ICMP] = &hicn_ops_icmp, + /* 6 */ [IPPROTO_TCP] = &hicn_ops_tcp, + /* 41 */ [IPPROTO_IPV6] = &hicn_ops_ipv6, + /* 51 */ [IPPROTO_AH] = &hicn_ops_ah, + /* 58 */ [IPPROTO_ICMPV6] = &hicn_ops_icmp, + [IPPROTO_NONE] = &hicn_ops_none, +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/ops.h b/lib/src/ops.h new file mode 100755 index 000000000..d56e6ae4a --- /dev/null +++ b/lib/src/ops.h @@ -0,0 +1,624 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file base.h + * @brief Protocol-independent packet operations + */ + +#ifndef HICN_OPS_H +#define HICN_OPS_H + +#include + +#include "error.h" +#include "header.h" +#include "name.h" + +/* + * hICN operations on packets + * + * All prototypes take an hicn_type_t parameter as their first argument, as this + * decides the sequence of protocols that are being used by the different + * operations. + */ + +typedef struct hicn_ops_s +{ + /** Protocol name */ + const char *name; + + /** + * @brief Initialize the headers of the hicn packet + * @param [in] type - hICN packet type + * @param [in,out] h - Buffer holding the packet + */ + int (*init_packet_header) (hicn_type_t type, hicn_protocol_t * h); + + /** + * @brief Retrieves an Interest locator + * @param [in] type - hICN packet type + * @param [in] h - Buffer holding the Interest packet + * @param [out] ip_address - Retrieved locator + * @return hICN error code + */ + int (*get_interest_locator) (hicn_type_t type, const hicn_protocol_t * h, + ip46_address_t * ip_address); + + /** + * @brief Sets an Interest locator + * @param [in] type - hICN packet type + * @param [in,out] h - Buffer holding the Interest packet + * @param [in] ip_address - Locator to set + * @return hICN error code + */ + int (*set_interest_locator) (hicn_type_t type, hicn_protocol_t * h, + const ip46_address_t * ip_address); + + /** + * @brief Retrieves an Interest name + * @param [in] type - hICN packet type + * @param [in] h - Buffer holding the Interest packet + * @param [out] name - Retrieved name + * @return hICN error code + */ + int (*get_interest_name) (hicn_type_t type, const hicn_protocol_t * h, + hicn_name_t * name); + + /** + * @brief Sets an Interest name + * @param [in] type - hICN packet type + * @param [in,out] h - Buffer holding the Interest packet + * @param [in] name - Name to set + * @return hICN error code + */ + int (*set_interest_name) (hicn_type_t type, hicn_protocol_t * h, + const hicn_name_t * name); + + /** + * @brief Retrieves an Interest name suffix + * @param [in] type - hICN packet type + * @param [in] h - Buffer holding the Interest packet + * @param [out] suffix - Retrieved name suffix + * @return hICN error code + */ + int (*get_interest_name_suffix) (hicn_type_t type, + const hicn_protocol_t * h, + hicn_name_suffix_t * suffix); + + /** + * @brief Sets an Interest name suffix + * @param [in] type - hICN packet type + * @param [in,out] h - Buffer holding the Interest packet + * @param [in] suffix - Name suffix to set + * @return hICN error code + */ + int (*set_interest_name_suffix) (hicn_type_t type, hicn_protocol_t * h, + const hicn_name_suffix_t * suffix); + + /** + * @brief Clear the necessary Interest fields in order to hash it + * @param [in] type - hICN packet type + * @param [in,out] h - Buffer holding the Interest packet + * @return hICN error code + */ + int (*reset_interest_for_hash) (hicn_type_t type, hicn_protocol_t * h); + + /** + * @brief Retrieves a Data locator + * @param [in] type - hICN packet type + * @param [in] h - Buffer holding the Data packet + * @param [out] ip_address - Retrieved locator + * @return hICN error code + */ + int (*get_data_locator) (hicn_type_t type, const hicn_protocol_t * h, + ip46_address_t * ip_address); + + /** + * @brief Sets a Data locator + * @param [in] type - hICN packet type + * @param [in,out] h - Buffer holding the Data packet + * @param [in] ip_address - Locator to set + * @return hICN error code + */ + int (*set_data_locator) (hicn_type_t type, hicn_protocol_t * h, + const ip46_address_t * ip_address); + + /** + * @brief Retrieves a Data name + * @param [in] type - hICN packet type + * @param [in] h - Buffer holding the Data packet + * @param [out] name - Retrieved name + * @return hICN error code + */ + int (*get_data_name) (hicn_type_t type, const hicn_protocol_t * h, + hicn_name_t * name); + + /** + * @brief Sets a Data name + * @param [in] type - hICN packet type + * @param [in,out] h - Buffer holding the Data packet + * @param [in] name - Name to set + * @return hICN error code + */ + int (*set_data_name) (hicn_type_t type, hicn_protocol_t * h, + const hicn_name_t * name); + + /** + * @brief Retrieves a Data name suffix + * @param [in] type - hICN packet type + * @param [in] h - Buffer holding the Data packet + * @param [out] suffix - Retrieved name suffix + * @return hICN error code + */ + int (*get_data_name_suffix) (hicn_type_t type, const hicn_protocol_t * h, + hicn_name_suffix_t * suffix); + + /** + * @brief Sets a Data name suffix + * @param [in] type - hICN packet type + * @param [in,out] h - Buffer holding the Data packet + * @param [in] suffix - Name suffix to set + * @return hICN error code + */ + int (*set_data_name_suffix) (hicn_type_t type, hicn_protocol_t * h, + const hicn_name_suffix_t * suffix); + + /** + * @brief Retrieves a Data pathlabel + * @param [in] type - hICN packet type + * @param [in] h - Buffer holding the Data packet + * @param [out] pathlabel - Retrieved pathlabel + * @return hICN error code + */ + int (*get_data_pathlabel) (hicn_type_t type, const hicn_protocol_t * h, + u32 * pathlabel); + + /** + * @brief Sets a Data pathlabel + * @param [in] type - hICN packet type + * @param [in,out] h - Buffer holding the Data packet + * @param [in] pathlabel - Pathlabel to set + * @return hICN error code + */ + int (*set_data_pathlabel) (hicn_type_t type, hicn_protocol_t * h, + const u32 pathlabel); + + /** + * @brief Update a Data pathlabel with a new face identifier + * @param [in] type - hICN packet type + * @param [in,out] h - Buffer holding the Data packet + * @param [in] pathlabel - Face identifier used to update pathlabel + * @return hICN error code + */ + int (*update_data_pathlabel) (hicn_type_t type, hicn_protocol_t * h, + const hicn_faceid_t face_id); + + /** + * @brief Clear the necessary Data fields in order to hash it + * @param [in] type - hICN packet type + * @param [in,out] h - Buffer holding the Data packet + * @return hICN error code + */ + int (*reset_data_for_hash) (hicn_type_t type, hicn_protocol_t * h); + + /** + * @brief Retrieves an Interest or Data lifetime + * @param [in] type - hICN packet type + * @param [in] h - Buffer holding the Interest or Data packet + * @param [out] pathlabel - Retrieved lifetime + * @return hICN error code + */ + int (*get_lifetime) (hicn_type_t type, const hicn_protocol_t * h, + hicn_lifetime_t * lifetime); + + /** + * @brief Sets an Interest or Data lifetime + * @param [in] type - hICN packet type + * @param [in,out] h - Buffer holding the Interest or Data packet + * @param [in] pathlabel - Lifetime to set + * @return hICN error code + */ + int (*set_lifetime) (hicn_type_t type, hicn_protocol_t * h, + const hicn_lifetime_t lifetime); + + /** + * @brief Update all checksums in packet headers + * @param [in] type - hICN packet type + * @param [in,out] h - Buffer holding the packet + * @param [in] partial_csum - Partial checksum (set to 0, used internally to + * carry intermediate values from IP pseudo-header) + * @param [in] payload_length - Payload length (can be set to 0, retrieved + * and used internally to carry payload length across protocol headers) + * @return hICN error code + */ + int (*update_checksums) (hicn_type_t type, hicn_protocol_t * h, + u16 partial_csum, size_t payload_length); + + /** + * @brief Validate all checksums in packet headers + * @param [in] type - hICN packet type + * @param [in] h - Buffer holding the packet + * @param [in] partial_csum - Partial checksum (set to 0, used internally to + * carry intermediate values from IP pseudo-header) + * @param [in] payload_length - Payload length (can be set to 0, retrieved + * and used internally to carry payload length across protocol headers) + * @return hICN error code + */ + int (*verify_checksums) (hicn_type_t type, hicn_protocol_t * h, + u16 partial_csum, size_t payload_length); + + /** + * @brief Rewrite an Interest packet header (locator) + * @param [in] type - hICN packet type + * @param [in] h - Buffer holding the Interest packet + * @param [in] addr_new - New locator + * @param [in] addr_old - Old locator (set to NULL, used internally to + * compute incremental checksums) + * @return hICN error code + */ + int (*rewrite_interest) (hicn_type_t type, hicn_protocol_t * h, + const ip46_address_t * addr_new, + ip46_address_t * addr_old); + + /** + * @brief Rewrite a Data packet header (locator + pathlabel) + * @param [in] type - hICN packet type + * @param [in] h - Buffer holding the Data packet + * @param [in] addr_new - New locator + * @param [in] addr_old - Old locator (set to NULL, used internally to + * compute incremental checksums) + * @param [in] face_id - Face identifier used to update pathlabel + * @return hICN error code + */ + int (*rewrite_data) (hicn_type_t type, hicn_protocol_t * h, + const ip46_address_t * addr_new, + ip46_address_t * addr_old, + const hicn_faceid_t face_id); + + /** + * @brief Return the packet length + * @param [in] type - hICN packet type + * @param [in] h - Buffer holding the packet + * @parma [out] length - Returned packet length + * @return hICN error code + */ + int (*get_length) (hicn_type_t type, const hicn_protocol_t * h, + size_t * length); + + /** + * @brief Return the current packet header length + * @param [in] type - hICN packet type + * @param [in] h - Buffer holding the packet + * @parma [out] header_length - Returned packet current header length + * @return hICN error code + */ + int (*get_current_header_length) (hicn_type_t type, + const hicn_protocol_t * h, + size_t * header_length); + + /** + * @brief Return the packet header length + * @param [in] type - hICN packet type + * @param [in] h - Buffer holding the packet + * @parma [out] header_length - Returned packet header length + * @return hICN error code + */ + int (*get_header_length) (hicn_type_t type, const hicn_protocol_t * h, + size_t * header_length); + + /** + * @brief Return the packet payload length + * @param [in] type - hICN packet type + * @param [in] h - Buffer holding the packet + * @parma [out] payload_length - Returned packet payload length + * @return hICN error code + */ + int (*get_payload_length) (hicn_type_t type, const hicn_protocol_t * h, + size_t * payload_length); + + /** + * @brief Sets the packet paylaod length + * @param [in] type - hICN packet type + * @param [in,out] h - Buffer holding the packet + * @parma [out] payload_length - Payload length to set + * @return hICN error code + */ + int (*set_payload_length) (hicn_type_t type, hicn_protocol_t * h, + size_t payload_length); + + /** + * @brief Retrieves an Interest or Data signature size + * @param [in] type - hICN packet type + * @param [in] h - Buffer holding the Interest or Data packet + * @param [out] signature_size - Retrieved signature size + * @return hICN error code + */ + int (*get_signature_size) (hicn_type_t type, const hicn_protocol_t * h, + size_t * signature_size); + + /** + * @brief Sets an Interest or Data signature size + * @param [in] type - hICN packet type + * @param [in,out] h - Buffer holding the Interest or Data packet + * @param [in] signature_size - Signature size to set + * @return hICN error code + */ + int (*set_signature_size) (hicn_type_t type, hicn_protocol_t * h, + size_t signature_size); + + /** + * @brief Sets the signature timestamp + * @param [in] type - hICN packet type + * @param [in,out] h - Buffer holding the Interest or Data packet + * @param [in] signature_timestamp - Signature timestamp to set + * @return hICN error code + */ + int (*set_signature_timestamp) (hicn_type_t type, hicn_protocol_t * h, + uint64_t signature_timestamp); + + /** + * @brief Gets the signature timestamp + * @param [in] type - hICN packet type + * @param [in,out] h - Buffer holding the Interest or Data packet + * @param [out] signature_timestamp - Retrieved signature timestamp + * @return hICN error code + */ + int (*get_signature_timestamp) (hicn_type_t type, const hicn_protocol_t * h, + uint64_t *signature_timestamp); + + /** + * @brief Sets the signature validation algorithm + * @param [in] type - hICN packet type + * @param [in,out] h - Buffer holding the Interest or Data packet + * @param [in] validation_algorithm - Validation algorithm enumeration + * @return hICN error code + */ + int (*set_validation_algorithm) (hicn_type_t type, hicn_protocol_t * h, + uint8_t validation_algorithm); + + /** + * @brief Gets the signature validation algorithm + * @param [in] type - hICN packet type + * @param [in,out] h - Buffer holding the Interest or Data packet + * @param [out] validation_algorithm - Retrieved validation_algorithm + * @return hICN error code + */ + int (*get_validation_algorithm) (hicn_type_t type, const hicn_protocol_t * h, + uint8_t *validation_algorithm); + + /** + * @brief Sets the key id + * @param [in] type - hICN packet type + * @param [in,out] h - Buffer holding the Interest or Data packet + * @param [in] key_id - Key id first byte address + * @return hICN error code + */ + int (*set_key_id) (hicn_type_t type, hicn_protocol_t * h, + uint8_t *key_id); + + /** + * @brief Gets the key id + * @param [in] type - hICN packet type + * @param [in,out] h - Buffer holding the Interest or Data packet + * @param [out] key_id - Retrieved key id first byte address + * @return hICN error code + */ + int (*get_key_id) (hicn_type_t type, hicn_protocol_t * h, + uint8_t **key_id, uint8_t *key_id_size); + +} hicn_ops_t; + +#define DECLARE_HICN_OPS(protocol) \ + const hicn_ops_t hicn_ops_ ## protocol = { \ + .init_packet_header = protocol ## _init_packet_header, \ + .get_interest_locator = protocol ## _get_interest_locator, \ + .set_interest_locator = protocol ## _set_interest_locator, \ + .get_interest_name = protocol ## _get_interest_name, \ + .set_interest_name = protocol ## _set_interest_name, \ + .get_interest_name_suffix = protocol ## _get_interest_name_suffix, \ + .set_interest_name_suffix = protocol ## _set_interest_name_suffix, \ + .reset_interest_for_hash = protocol ## _reset_interest_for_hash, \ + .get_data_locator = protocol ## _get_data_locator, \ + .set_data_locator = protocol ## _set_data_locator, \ + .get_data_name = protocol ## _get_data_name, \ + .set_data_name = protocol ## _set_data_name, \ + .get_data_name_suffix = protocol ## _get_data_name_suffix, \ + .set_data_name_suffix = protocol ## _set_data_name_suffix, \ + .get_data_pathlabel = protocol ## _get_data_pathlabel, \ + .set_data_pathlabel = protocol ## _set_data_pathlabel, \ + .update_data_pathlabel = protocol ## _update_data_pathlabel, \ + .reset_data_for_hash = protocol ## _reset_data_for_hash, \ + .get_lifetime = protocol ## _get_lifetime, \ + .set_lifetime = protocol ## _set_lifetime, \ + .update_checksums = protocol ## _update_checksums, \ + .verify_checksums = protocol ## _verify_checksums, \ + .rewrite_interest = protocol ## _rewrite_interest, \ + .rewrite_data = protocol ## _rewrite_data, \ + .get_length = protocol ## _get_length, \ + .get_current_header_length= protocol ## _get_current_header_length, \ + .get_header_length = protocol ## _get_header_length, \ + .get_payload_length = protocol ## _get_payload_length, \ + .set_payload_length = protocol ## _set_payload_length, \ + .get_signature_size = protocol ## _get_signature_size, \ + .set_signature_size = protocol ## _set_signature_size, \ + .set_signature_timestamp = protocol ## _set_signature_timestamp, \ + .get_signature_timestamp = protocol ## _get_signature_timestamp, \ + .set_validation_algorithm = protocol ## _set_validation_algorithm, \ + .get_validation_algorithm = protocol ## _get_validation_algorithm, \ + .set_key_id = protocol ## _set_key_id, \ + .get_key_id = protocol ## _get_key_id, \ + } + +/** + * @brief Protocol-independent packet operations VFT + * NOTE: The following declarations should be kept in order + */ +extern const hicn_ops_t *const hicn_ops_vft[]; + +/* + * Helpers for writing recursive protocol operations on packet headers + * + * NOTE : we cannot use a shift operation as IPPROTO_NONE != 0 (and 0 is IPv4...) + */ +always_inline hicn_type_t +TYPE_POP (hicn_type_t type) +{ + return (hicn_type_t) + { + { + .l1 = type.l2,.l2 = type.l3,.l3 = type.l4,.l4 = IPPROTO_NONE,} + }; +} + +always_inline hicn_protocol_t * +PAYLOAD (hicn_type_t type, const hicn_protocol_t * h) +{ + size_t header_length; + int rc = hicn_ops_vft[type.l1]->get_current_header_length (type, h, + &header_length); + if (rc < 0) + return NULL; + return (hicn_protocol_t *) ((u8 *) h + header_length); +} + +#define CHILD_OPS(f, type, h, ...) (hicn_ops_vft[type.l2]->f(TYPE_POP(type), PAYLOAD(type, h), ## __VA_ARGS__)) + +/** Shortcuts to entry points in VFT */ +#define HICN_OPS4 hicn_ops_vft[IPPROTO_IP] +#define HICN_OPS6 hicn_ops_vft[IPPROTO_IPV6] + +/* Helpers for simple declarations */ + +#define DECLARE_init_packet_header(protocol, error) \ + int protocol ## _init_packet_header(hicn_type_t type, hicn_protocol_t * h) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_get_interest_locator(protocol, error) \ + int protocol ## _get_interest_locator(hicn_type_t type, const hicn_protocol_t * h, ip46_address_t * ip_address) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_set_interest_locator(protocol, error) \ + int protocol ## _set_interest_locator(hicn_type_t type, hicn_protocol_t * h, const ip46_address_t * ip_address) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_get_interest_name(protocol, error) \ + int protocol ## _get_interest_name(hicn_type_t type, const hicn_protocol_t * h, hicn_name_t * name) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_set_interest_name(protocol, error) \ + int protocol ## _set_interest_name(hicn_type_t type, hicn_protocol_t * h, const hicn_name_t * name) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_get_interest_name_suffix(protocol, error) \ + int protocol ## _get_interest_name_suffix(hicn_type_t type, const hicn_protocol_t * h, hicn_name_suffix_t * suffix) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_set_interest_name_suffix(protocol, error) \ + int protocol ## _set_interest_name_suffix(hicn_type_t type, hicn_protocol_t * h, const hicn_name_suffix_t * suffix) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_reset_interest_for_hash(protocol, error) \ + int protocol ## _reset_interest_for_hash(hicn_type_t type, hicn_protocol_t * h) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_get_data_locator(protocol, error) \ + int protocol ## _get_data_locator(hicn_type_t type, const hicn_protocol_t * h, ip46_address_t * ip_address) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_set_data_locator(protocol, error) \ + int protocol ## _set_data_locator(hicn_type_t type, hicn_protocol_t * h, const ip46_address_t * ip_address) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_get_data_name(protocol, error) \ + int protocol ## _get_data_name(hicn_type_t type, const hicn_protocol_t * h, hicn_name_t * name) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_set_data_name(protocol, error) \ + int protocol ## _set_data_name(hicn_type_t type, hicn_protocol_t * h, const hicn_name_t * name) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_get_data_name_suffix(protocol, error) \ + int protocol ## _get_data_name_suffix(hicn_type_t type, const hicn_protocol_t * h, hicn_name_suffix_t * suffix) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_set_data_name_suffix(protocol, error) \ + int protocol ## _set_data_name_suffix(hicn_type_t type, hicn_protocol_t * h, const hicn_name_suffix_t * suffix) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_get_data_pathlabel(protocol, error) \ + int protocol ## _get_data_pathlabel(hicn_type_t type, const hicn_protocol_t * h, u32 * pathlabel) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_set_data_pathlabel(protocol, error) \ + int protocol ## _set_data_pathlabel(hicn_type_t type, hicn_protocol_t * h, const u32 pathlabel) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_update_data_pathlabel(protocol, error) \ + int protocol ## _update_data_pathlabel(hicn_type_t type, hicn_protocol_t * h, const hicn_faceid_t face_id) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_reset_data_for_hash(protocol, error) \ + int protocol ## _reset_data_for_hash(hicn_type_t type, hicn_protocol_t * h) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_get_lifetime(protocol, error) \ + int protocol ## _get_lifetime(hicn_type_t type, const hicn_protocol_t * h, hicn_lifetime_t * lifetime) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_set_lifetime(protocol, error) \ + int protocol ## _set_lifetime(hicn_type_t type, hicn_protocol_t * h, const hicn_lifetime_t lifetime) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_update_checksums(protocol, error) \ + int protocol ## _update_checksums(hicn_type_t type, hicn_protocol_t * h, u16 partial_csum, size_t payload_length) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_verify_checksums(protocol, error) \ + int protocol ## _verify_checksums(hicn_type_t type, hicn_protocol_t * h, u16 partial_csum, size_t payload_length) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_rewrite_interest(protocol, error) \ + int protocol ## _rewrite_interest(hicn_type_t type, hicn_protocol_t * h, const ip46_address_t * addr_new, ip46_address_t * addr_old) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_rewrite_data(protocol, error) \ + int protocol ## _rewrite_data(hicn_type_t type, hicn_protocol_t * h, const ip46_address_t * addr_new, ip46_address_t * addr_old, const hicn_faceid_t face_id) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_get_length(protocol, error) \ + int protocol ## _get_length(hicn_type_t type, const hicn_protocol_t * h, size_t * length) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_get_current_header_length(protocol, error) \ + int protocol ## _get_current_header_length(hicn_type_t type, const hicn_protocol_t * h, size_t * header_length) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_get_header_length(protocol, error) \ + int protocol ## _get_header_length(hicn_type_t type, const hicn_protocol_t * h, size_t * header_length) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_get_payload_length(protocol, error) \ + int protocol ## _get_payload_length(hicn_type_t type, const hicn_protocol_t * h, size_t * payload_length) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_set_payload_length(protocol, error) \ + int protocol ## _set_payload_length(hicn_type_t type, hicn_protocol_t * h, size_t payload_length) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_get_signature_size(protocol, error) \ + int protocol ## _get_signature_size(hicn_type_t type, const hicn_protocol_t * h, size_t * signature_size) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_set_signature_size(protocol, error) \ + int protocol ## _set_signature_size(hicn_type_t type, hicn_protocol_t * h, size_t signature_size) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_set_signature_timestamp(protocol, error) \ + int protocol ## _set_signature_timestamp(hicn_type_t type, hicn_protocol_t * h, uint64_t signature_timestamp) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_get_signature_timestamp(protocol, error) \ + int protocol ## _get_signature_timestamp(hicn_type_t type, const hicn_protocol_t * h, uint64_t * signature_timestamp) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_set_validation_algorithm(protocol, error) \ + int protocol ## _set_validation_algorithm(hicn_type_t type, hicn_protocol_t * h, uint8_t validation_algorithm) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_get_validation_algorithm(protocol, error) \ + int protocol ## _get_validation_algorithm(hicn_type_t type, const hicn_protocol_t * h, uint8_t * validation_algorithm) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_set_key_id(protocol, error) \ + int protocol ## _set_key_id(hicn_type_t type, hicn_protocol_t * h, uint8_t * key_id) { return HICN_LIB_ERROR_ ## error ; } + +#define DECLARE_get_key_id(protocol, error) \ + int protocol ## _get_key_id(hicn_type_t type, hicn_protocol_t * h, uint8_t ** key_id, uint8_t *key_id_size) { return HICN_LIB_ERROR_ ## error ; } + +#endif /* HICN_OPS_H */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/protocol.h b/lib/src/protocol.h new file mode 100755 index 000000000..a97cc99cf --- /dev/null +++ b/lib/src/protocol.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file protocol.h + * @brief Protocol header definitions + */ +#ifndef HICN_PROTOCOL_H +#define HICN_PROTOCOL_H + +#include "protocol/ah.h" +#include "protocol/ipv4.h" +#include "protocol/ipv6.h" +#include "protocol/icmp.h" +#include "protocol/icmprd.h" +#include "protocol/tcp.h" +#include "protocol/udp.h" + +typedef union +{ + _ipv4_header_t ipv4; + _ipv6_header_t ipv6; + _tcp_header_t tcp; + _udp_header_t udp; + _icmp_header_t icmp; + _icmprd_header_t icmprd; + _ah_header_t ah; + void *bytes; +} hicn_protocol_t; + +#endif /* HICN_PROTOCOL_H */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/protocol/ah.c b/lib/src/protocol/ah.c new file mode 100755 index 000000000..f9ddf7775 --- /dev/null +++ b/lib/src/protocol/ah.c @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file protocol/ah.c + * @brief hICN operations for AH header + */ + +#include // memcpy +#include "../common.h" +#include "../error.h" +#include "../header.h" +#include "../ops.h" +#include "ah.h" + +DECLARE_get_interest_locator (ah, UNEXPECTED); +DECLARE_set_interest_locator (ah, UNEXPECTED); +DECLARE_get_interest_name (ah, UNEXPECTED); +DECLARE_set_interest_name (ah, UNEXPECTED); +DECLARE_get_interest_name_suffix (ah, UNEXPECTED); +DECLARE_set_interest_name_suffix (ah, UNEXPECTED); +DECLARE_get_data_locator (ah, UNEXPECTED); +DECLARE_set_data_locator (ah, UNEXPECTED); +DECLARE_get_data_name (ah, UNEXPECTED); +DECLARE_set_data_name (ah, UNEXPECTED); +DECLARE_get_data_name_suffix (ah, UNEXPECTED); +DECLARE_set_data_name_suffix (ah, UNEXPECTED); +DECLARE_get_data_pathlabel (ah, UNEXPECTED); +DECLARE_set_data_pathlabel (ah, UNEXPECTED); +DECLARE_update_data_pathlabel (ah, UNEXPECTED); +DECLARE_get_lifetime (ah, UNEXPECTED); +DECLARE_set_lifetime (ah, UNEXPECTED); +DECLARE_get_payload_length (ah, UNEXPECTED); +DECLARE_set_payload_length (ah, UNEXPECTED); + +int +ah_init_packet_header (hicn_type_t type, hicn_protocol_t * h) +{ + return HICN_LIB_ERROR_NOT_IMPLEMENTED; +} + +int +ah_reset_interest_for_hash (hicn_type_t type, hicn_protocol_t * h) +{ + size_t signature_size; + int rc = + hicn_ops_vft[type.l1]->get_signature_size (type, h, &signature_size); + if (rc < 0) + return rc; + memset (&(h->ah.validationPayload), 0, signature_size); + return CHILD_OPS (reset_interest_for_hash, type, h); +} + +int +ah_reset_data_for_hash (hicn_type_t type, hicn_protocol_t * h) +{ + size_t signature_size; + int rc = + hicn_ops_vft[type.l1]->get_signature_size (type, h, &signature_size); + if (rc < 0) + return rc; + memset (&(h->ah.validationPayload), 0, signature_size); + return CHILD_OPS (reset_interest_for_hash, type, h); +} + +int +ah_update_checksums (hicn_type_t type, hicn_protocol_t * h, u16 partial_csum, + size_t payload_length) +{ + /* Nothing to do as there is no checksum in AH */ + return HICN_LIB_ERROR_NONE; +} + +int +ah_verify_checksums (hicn_type_t type, hicn_protocol_t * h, u16 partial_csum, + size_t payload_length) +{ + /* Nothing to do as there is no checksum in AH */ + return HICN_LIB_ERROR_NONE; +} + +int +ah_rewrite_interest (hicn_type_t type, hicn_protocol_t * h, + const ip46_address_t * addr_new, + ip46_address_t * addr_old) +{ + /* Nothing to do on signature */ + return HICN_LIB_ERROR_NONE; +} + +int +ah_rewrite_data (hicn_type_t type, hicn_protocol_t * h, + const ip46_address_t * addr_new, ip46_address_t * addr_old, + const hicn_faceid_t face_id) +{ + /* Nothing to do on signature */ + return HICN_LIB_ERROR_NONE; +} + +int +ah_get_length (hicn_type_t type, const hicn_protocol_t * h, size_t * length) +{ + return HICN_LIB_ERROR_NOT_IMPLEMENTED; +} + +int +ah_get_current_header_length (hicn_type_t type, const hicn_protocol_t * h, + size_t * header_length) +{ + *header_length = AH_HDRLEN + (h->ah.payloadlen << 2); + return HICN_LIB_ERROR_NONE; +} + +int +ah_get_header_length (hicn_type_t type, const hicn_protocol_t * h, + size_t * header_length) +{ + size_t child_header_length = 0; + int rc = CHILD_OPS (get_header_length, type, h, &child_header_length); + if (rc < 0) + return rc; + *header_length = AH_HDRLEN + (h->ah.payloadlen << 2) + child_header_length; + return HICN_LIB_ERROR_NONE; +} + +int +ah_get_signature_size (hicn_type_t type, const hicn_protocol_t * h, + size_t * signature_size) +{ + *signature_size = h->ah.payloadlen << 2; + return HICN_LIB_ERROR_NONE; +} + +int +ah_set_signature_size (hicn_type_t type, hicn_protocol_t * h, + const size_t signature_size) +{ + h->ah.payloadlen = signature_size >> 2; + return HICN_LIB_ERROR_NONE; +} + +int +ah_set_signature_timestamp(hicn_type_t type, hicn_protocol_t * h, + uint64_t signature_timestamp) +{ + h->ah.timestamp_as_u64 = signature_timestamp; + return HICN_LIB_ERROR_NONE; +} + +int +ah_get_signature_timestamp (hicn_type_t type, const hicn_protocol_t * h, + uint64_t * signature_timestamp) +{ + *signature_timestamp = h->ah.timestamp_as_u64; + return HICN_LIB_ERROR_NONE; +} + +int +ah_set_validation_algorithm (hicn_type_t type, hicn_protocol_t * h, + uint8_t validation_algorithm) +{ + h->ah.validationAlgorithm = validation_algorithm; + return HICN_LIB_ERROR_NONE; +} + +int +ah_get_validation_algorithm (hicn_type_t type, const hicn_protocol_t * h, + uint8_t * validation_algorithm) +{ + *validation_algorithm = h->ah.validationAlgorithm; + return HICN_LIB_ERROR_NONE; +} + +int +ah_set_key_id (hicn_type_t type, hicn_protocol_t * h, + uint8_t *key_id) +{ + memcpy(h->ah.keyId, key_id, sizeof(h->ah.keyId)); + return HICN_LIB_ERROR_NONE; +} + +int +ah_get_key_id (hicn_type_t type, hicn_protocol_t * h, + uint8_t **key_id, uint8_t *key_id_size) +{ + *key_id = h->ah.keyId; + *key_id_size = sizeof(h->ah.keyId); + return HICN_LIB_ERROR_NONE; +} + +DECLARE_HICN_OPS (ah); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/protocol/ah.h b/lib/src/protocol/ah.h new file mode 100755 index 000000000..0b4171135 --- /dev/null +++ b/lib/src/protocol/ah.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file protocol/ah.h + * @brief AH packet header + */ +#ifndef HICN_PROTOCOL_AH_H +#define HICN_PROTOCOL_AH_H + +/* + * The TCP PSH flag is set to indicate TCP payload in fact contains a AH header + * with signature information for the packet + */ +#define AH_FLAG 0x10 + +typedef struct +{ + u8 nh; // (to match with reserved in IPSEC AH) + u8 payloadlen; // Len of signature/HMAC in 4-bytes words + union + { + u16 reserved; + struct + { + u8 validationAlgorithm; // As defined in parc_SignerAlgorithm.h + u8 unused; // Unused (to match with reserved in IPSEC AH) + }; + }; + union + { + struct + { + u32 spi; + u32 seq; + }; + union + { + u8 timestamp_as_u8[8]; + u64 timestamp_as_u64; + }; // Unix timestamp indicating when the signature has been calculated + + }; + // ICV would follow + u8 keyId[32]; // Hash of the pub key + /* 44 B + validationPayload */ + u8 validationPayload[0]; // Holds the signature +} _ah_header_t; + +#define AH_HDRLEN sizeof(_ah_header_t) + +#endif /* HICN_PROTOCOL_AH_H */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/protocol/icmp.c b/lib/src/protocol/icmp.c new file mode 100755 index 000000000..44b646fb2 --- /dev/null +++ b/lib/src/protocol/icmp.c @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "icmp.h" + +#include "../error.h" +#include "../ops.h" + +DECLARE_get_interest_locator (icmp, UNEXPECTED) +DECLARE_set_interest_locator (icmp, UNEXPECTED) +DECLARE_get_interest_name (icmp, UNEXPECTED) +DECLARE_set_interest_name (icmp, UNEXPECTED) +DECLARE_get_interest_name_suffix (icmp, UNEXPECTED) +DECLARE_set_interest_name_suffix (icmp, UNEXPECTED) +DECLARE_get_data_locator (icmp, UNEXPECTED) +DECLARE_set_data_locator (icmp, UNEXPECTED) +DECLARE_get_data_name (icmp, UNEXPECTED) +DECLARE_set_data_name (icmp, UNEXPECTED) +DECLARE_get_data_name_suffix (icmp, UNEXPECTED) +DECLARE_set_data_name_suffix (icmp, UNEXPECTED) +DECLARE_get_data_pathlabel (icmp, UNEXPECTED) +DECLARE_set_data_pathlabel (icmp, UNEXPECTED) +DECLARE_update_data_pathlabel (icmp, UNEXPECTED) +DECLARE_get_lifetime (icmp, UNEXPECTED) +DECLARE_set_lifetime (icmp, UNEXPECTED) +DECLARE_get_length (icmp, UNEXPECTED) +DECLARE_get_payload_length (icmp, UNEXPECTED) +DECLARE_set_payload_length (icmp, UNEXPECTED) + int icmp_init_packet_header (hicn_type_t type, hicn_protocol_t * h) +{ + h->icmp = (_icmp_header_t) + { + .type = 0,.code = 0,.csum = 0,}; + + return HICN_LIB_ERROR_NONE; // CHILD_OPS(init_packet_header, type, h->icmp); +} + +int +icmp_reset_interest_for_hash (hicn_type_t type, hicn_protocol_t * h) +{ + h->icmp.csum = 0; + + return CHILD_OPS (reset_interest_for_hash, type, h); +} + +int +icmp_reset_data_for_hash (hicn_type_t type, hicn_protocol_t * h) +{ + h->icmp.csum = 0; + + return CHILD_OPS (reset_data_for_hash, type, h); +} + +int +icmp_update_checksums (hicn_type_t type, hicn_protocol_t * h, + u16 partial_csum, size_t payload_length) +{ + return HICN_LIB_ERROR_NOT_IMPLEMENTED; +// h->icmp.csum = 0; +// h->icmp.csum = csum(h->bytes, TCP_HDRLEN + payload_length, ~partial_csum); +// +// return CHILD_OPS(update_checksums, type, h->icmp, 0, payload_length); +} + +int +icmp_verify_checksums (hicn_type_t type, hicn_protocol_t * h, + u16 partial_csum, size_t payload_length) +{ + return HICN_LIB_ERROR_NOT_IMPLEMENTED; +// if (csum(h->bytes, TCP_HDRLEN + payload_length, ~partial_csum) != 0) +// return HICN_LIB_ERROR_CORRUPTED_PACKET; +// return CHILD_OPS(verify_checksums, type, h->icmp, 0, payload_length); +} + +int +icmp_rewrite_interest (hicn_type_t type, hicn_protocol_t * h, + const ip46_address_t * addr_new, + ip46_address_t * addr_old) +{ + return HICN_LIB_ERROR_NOT_IMPLEMENTED; +// u16 *icmp_checksum = &(h->icmp.csum); +// +// /* +// * Padding fields are set to zero so we can apply checksum on the +// * whole struct by interpreting it as IPv6 in all cases +// * +// * v4 code would be: +// * csum = ip_csum_sub_even (*icmp_checksum, h->ipv4.saddr.as_u32); +// * csum = ip_csum_add_even (csum, h->ipv4.saddr.as_u32); +// */ +// u16 csum = ip_csum_sub_even (*icmp_checksum, h->ipv6.saddr.as_u64[0]); +// csum = ip_csum_sub_even (csum, h->ipv6.saddr.as_u64[1]); +// csum = ip_csum_add_even (csum, h->ipv6.saddr.as_u64[0]); +// csum = ip_csum_add_even (csum, h->ipv6.saddr.as_u64[1]); +// +// *icmp_checksum = ip_csum_fold (csum); +// +// return HICN_LIB_ERROR_NONE; +} + +int +icmp_rewrite_data (hicn_type_t type, hicn_protocol_t * h, + const ip46_address_t * addr_new, ip46_address_t * addr_old, + const hicn_faceid_t face_id) +{ + return HICN_LIB_ERROR_NOT_IMPLEMENTED; +// u16 *icmp_checksum = &(h->icmp.csum); +// +// /* +// * Padding fields are set to zero so we can apply checksum on the +// * whole struct by interpreting it as IPv6 in all cases +// * +// * v4 code would be: +// * csum = ip_csum_sub_even (*icmp_checksum, h->ipv4.saddr.as_u32); +// * csum = ip_csum_add_even (csum, h->ipv4.saddr.as_u32); +// */ +// u16 csum = ip_csum_sub_even (*icmp_checksum, addr_old->ip6.as_u64[0]); +// csum = ip_csum_sub_even (*icmp_checksum, addr_old->ip6.as_u64[1]); +// csum = ip_csum_add_even (csum, addr_new->ip6.as_u64[0]); +// csum = ip_csum_add_even (csum, addr_new->ip6.as_u64[1]); +// +// csum = ip_csum_sub_even (csum, h->icmp.pathlabel); +// icmp_update_data_pathlabel(type, h, face_id); +// csum = ip_csum_add_even (csum, h->icmp.pathlabel); +// +// *icmp_checksum = ip_csum_fold (csum); +// +// return HICN_LIB_ERROR_NONE; +} + +int +icmp_get_current_header_length (hicn_type_t type, const hicn_protocol_t * h, + size_t * header_length) +{ + *header_length = ICMP_HDRLEN; + return HICN_LIB_ERROR_NONE; +} + +int +icmp_get_header_length (hicn_type_t type, const hicn_protocol_t * h, + size_t * header_length) +{ + size_t child_header_length = 0; + int rc = CHILD_OPS (get_header_length, type, h, &child_header_length); + if (rc < 0) + return rc; + + *header_length = ICMP_HDRLEN + child_header_length; + return HICN_LIB_ERROR_NONE; +} + +int +icmp_get_signature_size (hicn_type_t type, const hicn_protocol_t * h, + size_t * signature_size) +{ + return CHILD_OPS (get_signature_size, type, h, signature_size); +} + +int +icmp_set_signature_size (hicn_type_t type, hicn_protocol_t * h, + size_t signature_size) +{ + return CHILD_OPS (set_signature_size, type, h, signature_size); +} + +int +icmp_set_signature_timestamp(hicn_type_t type, hicn_protocol_t * h, + uint64_t signature_timestamp) +{ + return CHILD_OPS (set_signature_timestamp, type, h, signature_timestamp); +} + +int +icmp_get_signature_timestamp (hicn_type_t type, const hicn_protocol_t * h, + uint64_t * signature_timestamp) +{ + return CHILD_OPS (get_signature_timestamp, type, h, signature_timestamp); +} + +int +icmp_set_validation_algorithm (hicn_type_t type, hicn_protocol_t * h, + uint8_t validation_algorithm) +{ + return CHILD_OPS (set_validation_algorithm, type, h, validation_algorithm); +} + +int +icmp_get_validation_algorithm (hicn_type_t type, const hicn_protocol_t * h, + uint8_t * validation_algorithm) +{ + return CHILD_OPS (get_validation_algorithm, type, h, validation_algorithm); +} + +int +icmp_set_key_id (hicn_type_t type, hicn_protocol_t * h, + uint8_t *key_id) +{ + return CHILD_OPS (set_key_id, type, h, key_id); +} + +int +icmp_get_key_id (hicn_type_t type, hicn_protocol_t * h, + uint8_t **key_id, uint8_t *key_id_size) +{ + return CHILD_OPS (get_key_id, type, h, key_id, key_id_size); +} + +DECLARE_HICN_OPS (icmp); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/protocol/icmp.h b/lib/src/protocol/icmp.h new file mode 100755 index 000000000..5a84995b6 --- /dev/null +++ b/lib/src/protocol/icmp.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file protocol/icmp.h + * @brief ICMP packet header + */ +#ifndef HICN_PROTOCOL_ICMP_H +#define HICN_PROTOCOL_ICMP_H + +#include "../common.h" + +typedef struct +{ + u8 type; + u8 code; + u16 csum; +} _icmp_header_t; + +typedef struct +{ + u8 type; + u8 code; + u16 csum; + union + { + struct + { + u16 id; + u16 sequence; + } echo; /* echo datagram */ + u32 gateway; /* gateway address */ + struct + { + u16 _unused; + u16 mtu; + } frag; /* path mtu discovery */ + struct + { + u16 expected_lbl; + u16 received_lbl; + } wldr_notification_lbl; + }; +} _icmp_wldr_header_t; + +#define ICMP_HDRLEN sizeof(_icmp_header_t) + +#endif /* HICN_PROTOCOL_ICMP_H */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/protocol/icmprd.h b/lib/src/protocol/icmprd.h new file mode 100755 index 000000000..c2f27d673 --- /dev/null +++ b/lib/src/protocol/icmprd.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file protocol/icmp-rd.c + * @brief hICN operations for ICMP Redirect header + */ +#ifndef HICN_PROTOCOL_ICMPRD_H +#define HICN_PROTOCOL_ICMPRD_H + +#include "../common.h" + +typedef struct __attribute__ ((__packed__)) +{ + ip4_address_t ip; + _ipv4_header_t iph; + u8 data[64]; +} _icmprd4_header_t; + +typedef struct __attribute__ ((__packed__)) +{ + u32 res; + ip6_address_t tgt; + ip6_address_t dst; +} _icmprd_header_t; + +#endif /* HICN_PROTOCOL_ICMPRD_H */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/protocol/ipv4.c b/lib/src/protocol/ipv4.c new file mode 100755 index 000000000..7c6af127d --- /dev/null +++ b/lib/src/protocol/ipv4.c @@ -0,0 +1,452 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file protocol/ipv4.c + * @brief hICN operations for IPv4 header + * + * NOTE: IPv4 options (affecting the header size) are currently not supported. + */ + +#include +#include +#include +#include + +#include "../error.h" +#include "../ops.h" +#include "../common.h" +#include "../header.h" + +#include "ipv4.h" + +int ipv4_get_payload_length (hicn_type_t type, const hicn_protocol_t * h, + size_t * payload_length); + +int +ipv4_init_packet_header (hicn_type_t type, hicn_protocol_t * h) +{ + size_t total_header_length; + int rc = + hicn_ops_vft[type.l1]->get_header_length (type, h, &total_header_length); + if (rc < 0) + return rc; + + h->ipv4 = (_ipv4_header_t) + { + .version_ihl = + (IPV4_DEFAULT_VERSION << 4) | (0x0f & IPV4_DEFAULT_IHL),.tos = + IPV4_DEFAULT_TOS,.len = htons ((u16) total_header_length),.id = + htons (IPV4_DEFAULT_ID),.frag_off = + htons (IPV4_DEFAULT_FRAG_OFF),.ttl = HICN_DEFAULT_TTL,.protocol = + type.l2,.csum = 0,.saddr.as_u32 = 0,.daddr.as_u32 = 0,}; + + return CHILD_OPS (init_packet_header, type, h); +} + +int +ipv4_get_interest_locator (hicn_type_t type, const hicn_protocol_t * h, + ip46_address_t * ip_address) +{ + ip_address->ip4 = h->ipv4.saddr; + return HICN_LIB_ERROR_NONE; +} + +int +ipv4_set_interest_locator (hicn_type_t type, hicn_protocol_t * h, + const ip46_address_t * ip_address) +{ + h->ipv4.saddr = ip_address->ip4; + return HICN_LIB_ERROR_NONE; +} + +int +ipv4_get_interest_name (hicn_type_t type, const hicn_protocol_t * h, + hicn_name_t * name) +{ + name->ip4.prefix_as_ip4 = h->ipv4.daddr; +#ifndef HICN_VPP_PLUGIN + name->type = HNT_CONTIGUOUS_V4; + name->len = HICN_V4_NAME_LEN; +#endif /* HICN_VPP_PLUGIN */ + return CHILD_OPS (get_interest_name_suffix, type, h, &(name->ip4.suffix)); +} + +int +ipv4_set_interest_name (hicn_type_t type, hicn_protocol_t * h, + const hicn_name_t * name) +{ + h->ipv4.daddr = name->ip4.prefix_as_ip4; + return CHILD_OPS (set_interest_name_suffix, type, h, &(name->ip4.suffix)); +} + +int +ipv4_get_interest_name_suffix (hicn_type_t type, const hicn_protocol_t * h, + hicn_name_suffix_t * suffix) +{ + return CHILD_OPS (set_interest_name_suffix, type, h, suffix); +} + +int +ipv4_set_interest_name_suffix (hicn_type_t type, hicn_protocol_t * h, + const hicn_name_suffix_t * suffix) +{ + return CHILD_OPS (set_interest_name_suffix, type, h, suffix); +} + +int +ipv4_reset_interest_for_hash (hicn_type_t type, hicn_protocol_t * h) +{ + /* Sets everything to 0 up to IP destination address */ + memset (&(h->ipv4), 0, 16); + + return CHILD_OPS (reset_interest_for_hash, type, h); +} + +int +ipv4_get_data_locator (hicn_type_t type, const hicn_protocol_t * h, + ip46_address_t * ip_address) +{ + ip_address->ip4 = h->ipv4.daddr; + return HICN_LIB_ERROR_NONE; +} + +int +ipv4_set_data_locator (hicn_type_t type, hicn_protocol_t * h, + const ip46_address_t * ip_address) +{ + h->ipv4.daddr = ip_address->ip4; + return HICN_LIB_ERROR_NONE; +} + +int +ipv4_get_data_name (hicn_type_t type, const hicn_protocol_t * h, + hicn_name_t * name) +{ + name->ip4.prefix_as_ip4 = h->ipv4.saddr; +#ifndef HICN_VPP_PLUGIN + name->type = HNT_CONTIGUOUS_V4; + name->len = HICN_V4_NAME_LEN; +#endif /* HICN_VPP_PLUGIN */ + return CHILD_OPS (get_data_name_suffix, type, h, &(name->ip4.suffix)); +} + +int +ipv4_set_data_name (hicn_type_t type, hicn_protocol_t * h, + const hicn_name_t * name) +{ + h->ipv4.saddr = name->ip4.prefix_as_ip4; + return CHILD_OPS (set_data_name_suffix, type, h, &(name->ip4.suffix)); +} + +int +ipv4_get_data_name_suffix (hicn_type_t type, const hicn_protocol_t * h, + hicn_name_suffix_t * suffix) +{ + return CHILD_OPS (get_data_name_suffix, type, h, suffix); +} + +int +ipv4_set_data_name_suffix (hicn_type_t type, hicn_protocol_t * h, + const hicn_name_suffix_t * suffix) +{ + return CHILD_OPS (set_data_name_suffix, type, h, suffix); +} + +int +ipv4_get_data_pathlabel (hicn_type_t type, const hicn_protocol_t * h, + u32 * pathlabel) +{ + return CHILD_OPS (get_data_pathlabel, type, h, pathlabel); +} + +int +ipv4_set_data_pathlabel (hicn_type_t type, hicn_protocol_t * h, + const u32 pathlabel) +{ + return CHILD_OPS (set_data_pathlabel, type, h, pathlabel); +} + +int +ipv4_update_data_pathlabel (hicn_type_t type, hicn_protocol_t * h, + const hicn_faceid_t face_id) +{ + return CHILD_OPS (update_data_pathlabel, type, h, face_id); +} + +int +ipv4_reset_data_for_hash (hicn_type_t type, hicn_protocol_t * h) +{ + /* Sets everything to 0 up to source address */ + memset (&h->ipv4, 0, 12); + /* Clears destination address */ + memset (&(h->ipv4.daddr), 0, 4); + + return CHILD_OPS (reset_data_for_hash, type, h); +} + +int +ipv4_get_lifetime (hicn_type_t type, const hicn_protocol_t * h, + hicn_lifetime_t * lifetime) +{ + return CHILD_OPS (get_lifetime, type, h, lifetime); +} + +int +ipv4_set_lifetime (hicn_type_t type, hicn_protocol_t * h, + const hicn_lifetime_t lifetime) +{ + return CHILD_OPS (set_lifetime, type, h, lifetime); +} + +int +ipv4_update_checksums (hicn_type_t type, hicn_protocol_t * h, + u16 partial_csum, size_t payload_length) +{ + /* + * Checksum field is not accounted for in lower layers, so we can compute + * them in any order. Note that it is only a header checksum. + */ + h->ipv4.csum = 0; + h->ipv4.csum = csum (h, IPV4_HDRLEN, 0); + + /* Retrieve payload length if not specified, as it is not available later */ + if (payload_length == 0) + { + int rc = ipv4_get_payload_length (type, h, &payload_length); + if (rc < 0) + return rc; + } + + /* Ignore the payload if payload_length = ~0 */ + if (payload_length == ~0) + { + payload_length = 0; + } + + /* Build pseudo-header */ + ipv4_pseudo_header_t psh; + psh.ip_src = h->ipv4.saddr; + psh.ip_dst = h->ipv4.daddr; + /* Size is u32 and not u16, we cannot copy and need to care about endianness */ + psh.size = htons (ntohs (h->ipv4.len) - (u16) IPV4_HDRLEN); + psh.zero = 0; + psh.protocol = (u8) h->ipv4.protocol; + + /* Compute partial checksum based on pseudo-header */ + if (partial_csum != 0) + { + partial_csum = ~partial_csum; + } + partial_csum = csum (&psh, IPV4_PSHDRLEN, partial_csum); + + return CHILD_OPS (update_checksums, type, h, partial_csum, payload_length); +} + +int +ipv4_verify_checksums (hicn_type_t type, hicn_protocol_t * h, + u16 partial_csum, size_t payload_length) +{ + /* + * Checksum field is not accounted for in lower layers, so we can compute + * them in any order. Note that it is only a header checksum. + */ + if (csum (h, IPV4_HDRLEN, 0) != 0) + return HICN_LIB_ERROR_CORRUPTED_PACKET; + + /* Retrieve payload length if not specified, as it is not available later */ + if (payload_length == 0) + { + int rc = ipv4_get_payload_length (type, h, &payload_length); + if (rc < 0) + return rc; + } + + /* Build pseudo-header */ + ipv4_pseudo_header_t psh; + psh.ip_src = h->ipv4.saddr; + psh.ip_dst = h->ipv4.daddr; + /* Size is u32 and not u16, we cannot copy and need to care about endianness */ + psh.size = htons (ntohs (h->ipv4.len) - (u16) IPV4_HDRLEN); + psh.zero = 0; + psh.protocol = (u8) h->ipv4.protocol; + + /* Compute partial checksum based on pseudo-header */ + partial_csum = csum (&psh, IPV4_PSHDRLEN, 0); + + return CHILD_OPS (update_checksums, type, h, partial_csum, payload_length); +} + +int +ipv4_rewrite_interest (hicn_type_t type, hicn_protocol_t * h, + const ip46_address_t * addr_new, + ip46_address_t * addr_old) +{ + // ASSERT(addr_old == NULL); + addr_old->ip4 = h->ipv4.saddr; + addr_old->pad[0] = 0; + addr_old->pad[1] = 0; + addr_old->pad[2] = 0; + + h->ipv4.saddr = addr_new->ip4; + h->ipv4.csum = 0; + h->ipv4.csum = csum (&h->ipv4, IPV4_HDRLEN, 0); + + return CHILD_OPS (rewrite_interest, type, h, addr_new, addr_old); +} + +int +ipv4_rewrite_data (hicn_type_t type, hicn_protocol_t * h, + const ip46_address_t * addr_new, ip46_address_t * addr_old, + const hicn_faceid_t face_id) +{ + // ASSERT(addr_old == NULL); + addr_old->ip4 = h->ipv4.daddr; + addr_old->pad[0] = 0; + addr_old->pad[1] = 0; + addr_old->pad[2] = 0; + + h->ipv4.daddr = addr_new->ip4; + h->ipv4.csum = 0; + h->ipv4.csum = csum (&h->ipv4, IPV4_HDRLEN, 0); + + return CHILD_OPS (rewrite_data, type, h, addr_new, addr_old, face_id); +} + +int +ipv4_get_current_length (hicn_type_t type, const hicn_protocol_t * h, + size_t * header_length) +{ + *header_length = IPV4_HDRLEN; + return HICN_LIB_ERROR_NONE; +} + +int +ipv4_get_length (hicn_type_t type, const hicn_protocol_t * h, + size_t * header_length) +{ + *header_length = h->ipv4.len; + return HICN_LIB_ERROR_NONE; +} + +int +ipv4_get_current_header_length (hicn_type_t type, const hicn_protocol_t * h, + size_t * header_length) +{ + *header_length = IPV4_HDRLEN; + return HICN_LIB_ERROR_NONE; +} + +int +ipv4_get_header_length (hicn_type_t type, const hicn_protocol_t * h, + size_t * header_length) +{ + size_t child_header_length = 0; + int rc = CHILD_OPS (get_header_length, type, h, &child_header_length); + if (rc < 0) + return rc; + *header_length = IPV4_HDRLEN + child_header_length; + return HICN_LIB_ERROR_NONE; +} + +int +ipv4_get_payload_length (hicn_type_t type, const hicn_protocol_t * h, + size_t * payload_length) +{ + size_t child_header_length; + int rc = CHILD_OPS (get_header_length, type, h, &child_header_length); + if (rc < 0) + return rc; + *payload_length = htons (h->ipv4.len) - IPV4_HDRLEN - child_header_length; + return HICN_LIB_ERROR_NONE; +} + +int +ipv4_set_payload_length (hicn_type_t type, hicn_protocol_t * h, + size_t payload_length) +{ + size_t child_header_length; + int rc = CHILD_OPS (get_header_length, type, h, &child_header_length); + if (rc < 0) + return rc; + h->ipv4.len = htons (payload_length + IPV4_HDRLEN + child_header_length); + return HICN_LIB_ERROR_NONE; +} + +int +ipv4_get_signature_size (hicn_type_t type, const hicn_protocol_t * h, + size_t * signature_size) +{ + return CHILD_OPS (get_signature_size, type, h, signature_size); +} + +int +ipv4_set_signature_size (hicn_type_t type, hicn_protocol_t * h, + size_t signature_size) +{ + return CHILD_OPS (set_signature_size, type, h, signature_size); +} + +int +ipv4_set_signature_timestamp(hicn_type_t type, hicn_protocol_t * h, + uint64_t signature_timestamp) +{ + return CHILD_OPS (set_signature_timestamp, type, h, signature_timestamp); +} + +int +ipv4_get_signature_timestamp (hicn_type_t type, const hicn_protocol_t * h, + uint64_t * signature_timestamp) +{ + return CHILD_OPS (get_signature_timestamp, type, h, signature_timestamp); +} + +int +ipv4_set_validation_algorithm (hicn_type_t type, hicn_protocol_t * h, + uint8_t validation_algorithm) +{ + return CHILD_OPS (set_validation_algorithm, type, h, validation_algorithm); +} + +int +ipv4_get_validation_algorithm (hicn_type_t type, const hicn_protocol_t * h, + uint8_t * validation_algorithm) +{ + return CHILD_OPS (get_validation_algorithm, type, h, validation_algorithm); +} + +int +ipv4_set_key_id (hicn_type_t type, hicn_protocol_t * h, + uint8_t *key_id) +{ + return CHILD_OPS (set_key_id, type, h, key_id); +} + +int +ipv4_get_key_id (hicn_type_t type, hicn_protocol_t * h, + uint8_t **key_id, uint8_t *key_id_size) +{ + return CHILD_OPS (get_key_id, type, h, key_id, key_id_size); +} + +DECLARE_HICN_OPS (ipv4); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/protocol/ipv4.h b/lib/src/protocol/ipv4.h new file mode 100755 index 000000000..c57485b6c --- /dev/null +++ b/lib/src/protocol/ipv4.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HICN_PROTOCOL_IPV4 +#define HICN_PROTOCOL_IPV4 + +#include "../base.h" +#include "../common.h" +#include "../protocol.h" + +/* Headers were adapted from linux' definitions in netinet/ip.h */ + +typedef struct +{ + union + { + struct + { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + u8 ihl:4; + u8 version:4; +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + u8 version:4; + u8 ihl:4; +#else +#error "Unsupported endianness" +#endif + }; + + u8 version_ihl; + }; + u8 tos; + u16 len; + u16 id; + u16 frag_off; + u8 ttl; + u8 protocol; + u16 csum; + ip4_address_t saddr; + ip4_address_t daddr; +} _ipv4_header_t; + +#define ipv4_header_bytes(ipv4_header) (sizeof(u32) * (ipv4_header->version_ihl & 0xf)) + +#define IPV4_HDRLEN sizeof(_ipv4_header_t) + +typedef struct +{ + ip4_address_t ip_src; + ip4_address_t ip_dst; + u8 zero; + u8 protocol; + u16 size; +} ipv4_pseudo_header_t; + +#define IPV4_PSHDRLEN sizeof(ipv4_pseudo_header_t) + +/* Default field values */ +#define IPV4_DEFAULT_VERSION 4 +#define IPV4_DEFAULT_IHL 5 +#define IPV4_DEFAULT_TOS 0 +#define IPV4_DEFAULT_PAYLOAD_LENGTH 0 +#define IPV4_DEFAULT_ID 300 +#define IPV4_DEFAULT_FRAG_OFF 0x000 +#define IPV4_DEFAULT_TTL 64 +#define IPV4_DEFAULT_PROTOCOL IPPROTO_TCP +#define IPV4_DEFAULT_SRC_IP 0, 0, 0, 0 +#define IPV4_DEFAULT_DST_IP 0, 0, 0, 0 + + +#endif /* HICN_PROTOCOL_IPV4 */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/protocol/ipv6.c b/lib/src/protocol/ipv6.c new file mode 100755 index 000000000..41b00ec92 --- /dev/null +++ b/lib/src/protocol/ipv6.c @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "../common.h" +#include "../error.h" +#include "../ops.h" + +int +ipv6_get_payload_length (hicn_type_t type, const hicn_protocol_t * h, + size_t * payload_length); + +int +ipv6_init_packet_header (hicn_type_t type, hicn_protocol_t * h) +{ + size_t total_header_length; + int rc = CHILD_OPS (get_header_length, type, h, &total_header_length); + if (rc < 0) + return rc; + + /* *INDENT-OFF* */ + h->ipv6 = (_ipv6_header_t) + { + .saddr = {{ 0 }} + ,.daddr = {{ 0 }} + ,.version_class_flow = htonl ((IPV6_DEFAULT_VERSION << 28) | + (IPV6_DEFAULT_TRAFFIC_CLASS << 20) | + (IPV6_DEFAULT_FLOW_LABEL & 0xfffff)), + .len = htons ((u16) total_header_length), + .nxt = type.l2, + .hlim = HICN_DEFAULT_TTL, + }; + /* *INDENT-ON* */ + return CHILD_OPS (init_packet_header, type, h); +} + +int +ipv6_get_interest_locator (hicn_type_t type, const hicn_protocol_t * h, + ip46_address_t * ip_address) +{ + ip_address->ip6 = h->ipv6.saddr; + return HICN_LIB_ERROR_NONE; +} + +int +ipv6_set_interest_locator (hicn_type_t type, hicn_protocol_t * h, + const ip46_address_t * ip_address) +{ + h->ipv6.saddr = ip_address->ip6; + return HICN_LIB_ERROR_NONE; +} + +int +ipv6_get_interest_name (hicn_type_t type, const hicn_protocol_t * h, + hicn_name_t * name) +{ + name->ip6.prefix_as_ip6 = h->ipv6.daddr; +#ifndef HICN_VPP_PLUGIN + name->type = HNT_CONTIGUOUS_V6; + name->len = HICN_V6_NAME_LEN; +#endif /* HICN_VPP_PLUGIN */ + return CHILD_OPS (get_interest_name_suffix, type, h, &(name->ip6.suffix)); +} + +int +ipv6_set_interest_name (hicn_type_t type, hicn_protocol_t * h, + const hicn_name_t * name) +{ + h->ipv6.daddr = name->ip6.prefix_as_ip6; + return CHILD_OPS (set_interest_name_suffix, type, h, &(name->ip6.suffix)); +} + +int +ipv6_get_interest_name_suffix (hicn_type_t type, const hicn_protocol_t * h, + hicn_name_suffix_t * suffix) +{ + return CHILD_OPS (get_interest_name_suffix, type, h, suffix); +} + +int +ipv6_set_interest_name_suffix (hicn_type_t type, hicn_protocol_t * h, + const hicn_name_suffix_t * suffix) +{ + return CHILD_OPS (set_interest_name_suffix, type, h, suffix); +} + +int +ipv6_reset_interest_for_hash (hicn_type_t type, hicn_protocol_t * h) +{ + /* Sets everything to 0 up to IP destination address */ + memset (&(h->ipv6), 0, 24); + + return CHILD_OPS (reset_interest_for_hash, type, h); +} + +int +ipv6_get_data_locator (hicn_type_t type, const hicn_protocol_t * h, + ip46_address_t * ip_address) +{ + ip_address->ip6 = h->ipv6.daddr; + return HICN_LIB_ERROR_NONE; +} + +int +ipv6_set_data_locator (hicn_type_t type, hicn_protocol_t * h, + const ip46_address_t * ip_address) +{ + h->ipv6.daddr = ip_address->ip6; + return HICN_LIB_ERROR_NONE; +} + +int +ipv6_get_data_name (hicn_type_t type, const hicn_protocol_t * h, + hicn_name_t * name) +{ + name->ip6.prefix_as_ip6 = h->ipv6.saddr; +#ifndef HICN_VPP_PLUGIN + name->type = HNT_CONTIGUOUS_V6; + name->len = HICN_V6_NAME_LEN; +#endif /* HICN_VPP_PLUGIN */ + return CHILD_OPS (get_data_name_suffix, type, h, &(name->ip6.suffix)); +} + +int +ipv6_set_data_name (hicn_type_t type, hicn_protocol_t * h, + const hicn_name_t * name) +{ + h->ipv6.saddr = name->ip6.prefix_as_ip6; + return CHILD_OPS (set_data_name_suffix, type, h, &(name->ip6.suffix)); +} + +int +ipv6_get_data_name_suffix (hicn_type_t type, const hicn_protocol_t * h, + hicn_name_suffix_t * suffix) +{ + return CHILD_OPS (get_data_name_suffix, type, h, suffix); +} + +int +ipv6_set_data_name_suffix (hicn_type_t type, hicn_protocol_t * h, + const hicn_name_suffix_t * suffix) +{ + return CHILD_OPS (set_data_name_suffix, type, h, suffix); +} + +int +ipv6_get_data_pathlabel (hicn_type_t type, const hicn_protocol_t * h, + u32 * pathlabel) +{ + return CHILD_OPS (get_data_pathlabel, type, h, pathlabel); +} + +int +ipv6_set_data_pathlabel (hicn_type_t type, hicn_protocol_t * h, + const u32 pathlabel) +{ + return CHILD_OPS (set_data_pathlabel, type, h, pathlabel); +} + +int +ipv6_update_data_pathlabel (hicn_type_t type, hicn_protocol_t * h, + const hicn_faceid_t face_id) +{ + return CHILD_OPS (update_data_pathlabel, type, h, face_id); +} + +int +ipv6_reset_data_for_hash (hicn_type_t type, hicn_protocol_t * h) +{ + /* IP: Set everithing to 0 up to destination address */ + memset (&h->ipv6, 0, 8); + /* Clears destination address */ + memset (&(h->ipv6.daddr), 0, 16); + + return CHILD_OPS (reset_data_for_hash, type, h); +} + +int +ipv6_get_lifetime (hicn_type_t type, const hicn_protocol_t * h, + hicn_lifetime_t * lifetime) +{ + return CHILD_OPS (get_lifetime, type, h, lifetime); +} + +int +ipv6_set_lifetime (hicn_type_t type, hicn_protocol_t * h, + const hicn_lifetime_t lifetime) +{ + return CHILD_OPS (set_lifetime, type, h, lifetime); +} + +int +ipv6_update_checksums (hicn_type_t type, hicn_protocol_t * h, + u16 partial_csum, size_t payload_length) +{ + /* Retrieve payload length if not specified */ + if (payload_length == 0) + { + int rc = ipv6_get_payload_length (type, h, &payload_length); + if (rc < 0) + return rc; + } + + /* Ignore the payload if payload_length = ~0 */ + if (payload_length == ~0) + { + payload_length = 0; + } + + /* Build pseudo-header */ + ipv6_pseudo_header_t psh; + psh.ip_src = h->ipv6.saddr; + psh.ip_dst = h->ipv6.daddr; + /* Size is u32 and not u16, we cannot copy and need to care about endianness */ + psh.size = htonl (ntohs (h->ipv6.len)); + psh.zeros = 0; + psh.zero = 0; + psh.protocol = h->ipv6.nxt; + + /* Compute partial checksum based on pseudo-header */ + if (partial_csum != 0) + { + partial_csum = ~partial_csum; + } + partial_csum = csum (&psh, IPV6_PSHDRLEN, partial_csum); + + return CHILD_OPS (update_checksums, type, h, partial_csum, payload_length); +} + +int +ipv6_verify_checksums (hicn_type_t type, hicn_protocol_t * h, + u16 partial_csum, size_t payload_length) +{ + /* Retrieve payload length if not specified */ + if (payload_length == 0) + { + int rc = ipv6_get_payload_length (type, h, &payload_length); + if (rc < 0) + return rc; + } + + /* Build pseudo-header */ + ipv6_pseudo_header_t pseudo; + pseudo.ip_src = h->ipv6.saddr; + pseudo.ip_dst = h->ipv6.daddr; + /* Size is u32 and not u16, we cannot copy and need to care about endianness */ + pseudo.size = htonl (ntohs (h->ipv6.len)); + pseudo.zeros = 0; + pseudo.zero = 0; + pseudo.protocol = h->ipv6.nxt; + + /* Compute partial checksum based on pseudo-header */ + partial_csum = csum (&pseudo, IPV6_PSHDRLEN, 0); + + return CHILD_OPS (verify_checksums, type, h, partial_csum, payload_length); +} + +int +ipv6_rewrite_interest (hicn_type_t type, hicn_protocol_t * h, + const ip46_address_t * addr_new, + ip46_address_t * addr_old) +{ + // ASSERT(addr_old == NULL); + addr_old->ip6 = h->ipv6.saddr; + h->ipv6.saddr = addr_new->ip6; + + return CHILD_OPS (rewrite_interest, type, h, addr_new, addr_old); +} + +int +ipv6_rewrite_data (hicn_type_t type, hicn_protocol_t * h, + const ip46_address_t * addr_new, ip46_address_t * addr_old, + const hicn_faceid_t face_id) +{ + // ASSERT(addr_old == NULL); + addr_old->ip6 = h->ipv6.daddr; + h->ipv6.daddr = addr_new->ip6; + + return CHILD_OPS (rewrite_data, type, h, addr_new, addr_old, face_id); +} + +int +ipv6_get_length (hicn_type_t type, const hicn_protocol_t * h, + size_t * header_length) +{ + *header_length = IPV6_HDRLEN + ntohs (h->ipv6.len); + return HICN_LIB_ERROR_NONE; +} + +int +ipv6_get_current_header_length (hicn_type_t type, const hicn_protocol_t * h, + size_t * header_length) +{ + *header_length = IPV6_HDRLEN; + return HICN_LIB_ERROR_NONE; +} + +int +ipv6_get_header_length (hicn_type_t type, const hicn_protocol_t * h, + size_t * header_length) +{ + size_t child_header_length = 0; + int rc = CHILD_OPS (get_header_length, type, h, &child_header_length); + if (rc < 0) + return rc; + *header_length = IPV6_HDRLEN + child_header_length; + return HICN_LIB_ERROR_NONE; +} + +int +ipv6_get_payload_length (hicn_type_t type, const hicn_protocol_t * h, + size_t * payload_length) +{ + size_t child_header_length; + int rc = CHILD_OPS (get_header_length, type, h, &child_header_length); + if (rc < 0) + return rc; + *payload_length = ntohs (h->ipv6.len) - child_header_length; + return HICN_LIB_ERROR_NONE; +} + +int +ipv6_set_payload_length (hicn_type_t type, hicn_protocol_t * h, + size_t payload_length) +{ + size_t child_header_length; + int rc = CHILD_OPS (get_header_length, type, h, &child_header_length); + if (rc < 0) + return rc; + h->ipv6.len = htons (payload_length + child_header_length); + return HICN_LIB_ERROR_NONE; +} + +int +ipv6_get_signature_size (hicn_type_t type, const hicn_protocol_t * h, + size_t * signature_size) +{ + return CHILD_OPS (get_signature_size, type, h, signature_size); +} + +int +ipv6_set_signature_size (hicn_type_t type, hicn_protocol_t * h, + size_t signature_size) +{ + return CHILD_OPS (set_signature_size, type, h, signature_size); +} + +int +ipv6_set_signature_timestamp(hicn_type_t type, hicn_protocol_t * h, + uint64_t signature_timestamp) +{ + return CHILD_OPS (set_signature_timestamp, type, h, signature_timestamp); +} + +int +ipv6_get_signature_timestamp (hicn_type_t type, const hicn_protocol_t * h, + uint64_t * signature_timestamp) +{ + return CHILD_OPS (get_signature_timestamp, type, h, signature_timestamp); +} + +int +ipv6_set_validation_algorithm (hicn_type_t type, hicn_protocol_t * h, + uint8_t validation_algorithm) +{ + return CHILD_OPS (set_validation_algorithm, type, h, validation_algorithm); +} + +int +ipv6_get_validation_algorithm (hicn_type_t type, const hicn_protocol_t * h, + uint8_t * validation_algorithm) +{ + return CHILD_OPS (get_validation_algorithm, type, h, validation_algorithm); +} + +int +ipv6_set_key_id (hicn_type_t type, hicn_protocol_t * h, + uint8_t *key_id) +{ + return CHILD_OPS (set_key_id, type, h, key_id); +} + +int +ipv6_get_key_id (hicn_type_t type, hicn_protocol_t * h, + uint8_t **key_id, uint8_t *key_id_size) +{ + return CHILD_OPS (get_key_id, type, h, key_id, key_id_size); +} + +DECLARE_HICN_OPS (ipv6); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/protocol/ipv6.h b/lib/src/protocol/ipv6.h new file mode 100755 index 000000000..28a1aa47f --- /dev/null +++ b/lib/src/protocol/ipv6.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HICN_PROTOCOL_IPV6_H +#define HICN_PROTOCOL_IPV6_H + +#include "../common.h" + +typedef struct +{ + union + { + struct + { + u32 version_class_flow; /* version, traffic class and 20 bits of flow-ID */ + u16 len; /* payload length */ + u8 nxt; /* next header */ + u8 hlim; /* hop limit */ + }; + u8 vfc; /* 4 bits version, top 4 bits class */ + }; + ip6_address_t saddr; /* source address */ + ip6_address_t daddr; /* destination address */ +} _ipv6_header_t; + + +#define IPV6_HDRLEN sizeof(_ipv6_header_t) + +typedef struct +{ + ip6_address_t ip_src; + ip6_address_t ip_dst; + u32 size; + u16 zeros; + u8 zero; + u8 protocol; +} ipv6_pseudo_header_t; + +#define IPV6_PSHDRLEN sizeof(ipv6_pseudo_header_t) + +/* Default field values */ +#define IPV6_DEFAULT_VERSION 6 +#define IPV6_DEFAULT_TRAFFIC_CLASS 0 +#define IPV6_DEFAULT_FLOW_LABEL 0 +#define IPV6_DEFAULT_PAYLOAD_LENGTH 0 + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/protocol/tcp.c b/lib/src/protocol/tcp.c new file mode 100755 index 000000000..2afc4f6f4 --- /dev/null +++ b/lib/src/protocol/tcp.c @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "tcp.h" + +#include "../error.h" +#include "../ops.h" + +#define TCP_DEFAULT_SRC_PORT 0x8000 +#define TCP_DEFAULT_DST_PORT 0x0080 +#define TCP_DEFAULT_WINDOW_SIZE 0 // In [2, 65535] +#define TCP_DEFAULT_HLEN 20 +#define TCP_DEFAULT_DATA_OFFSET_RES (TCP_DEFAULT_HLEN >> 2) << 4 +#define TCP_DEFAULT_CWR 0 +#define TCP_DEFAULT_ECE 0 +#define TCP_DEFAULT_URG 0 +#define TCP_DEFAULT_ACK 0 +#define TCP_DEFAULT_PSH 0 +#define TCP_DEFAULT_RST 0 +#define TCP_DEFAULT_SYN 1 +#define TCP_DEFAULT_FIN 0 + +DECLARE_get_interest_locator (tcp, UNEXPECTED); +DECLARE_set_interest_locator (tcp, UNEXPECTED); +DECLARE_get_interest_name (tcp, UNEXPECTED); +DECLARE_set_interest_name (tcp, UNEXPECTED); +DECLARE_get_data_locator (tcp, UNEXPECTED); +DECLARE_set_data_locator (tcp, UNEXPECTED); +DECLARE_get_data_name (tcp, UNEXPECTED); +DECLARE_set_data_name (tcp, UNEXPECTED); +DECLARE_get_length (tcp, UNEXPECTED); +DECLARE_get_payload_length (tcp, UNEXPECTED); +DECLARE_set_payload_length (tcp, UNEXPECTED); + +int +tcp_init_packet_header (hicn_type_t type, hicn_protocol_t * h) +{ + h->tcp = (_tcp_header_t) + { + .sport = htons (TCP_DEFAULT_SRC_PORT),.dport = + htons (TCP_DEFAULT_DST_PORT),.seq = 0,.seq_ack = + 0,.data_offset_and_reserved = TCP_DEFAULT_DATA_OFFSET_RES,.flags = + TCP_DEFAULT_CWR << 7 | TCP_DEFAULT_ECE << 6 | TCP_DEFAULT_URG << 5 | + TCP_DEFAULT_ACK << 4 | TCP_DEFAULT_PSH << 3 | TCP_DEFAULT_RST << 2 | + TCP_DEFAULT_SYN << 1 | TCP_DEFAULT_FIN << 0,.window = + htons (TCP_DEFAULT_WINDOW_SIZE),.csum = 0,.urg_ptr = 65000,}; + + return CHILD_OPS (init_packet_header, type, h); +} + +int +tcp_get_interest_name_suffix (hicn_type_t type, const hicn_protocol_t * h, + hicn_name_suffix_t * suffix) +{ + *suffix = ntohl (h->tcp.name_suffix); + return HICN_LIB_ERROR_NONE; +} + +int +tcp_set_interest_name_suffix (hicn_type_t type, hicn_protocol_t * h, + const hicn_name_suffix_t * suffix) +{ + h->tcp.name_suffix = htonl (*suffix); + + return HICN_LIB_ERROR_NONE; +} + +int +tcp_reset_interest_for_hash (hicn_type_t type, hicn_protocol_t * h) +{ + memset (&(h->tcp), 0, 4); + memset (&(h->tcp.seq_ack), 0, 12); + + return CHILD_OPS (reset_interest_for_hash, type, h); +} + + +int +tcp_get_data_name_suffix (hicn_type_t type, const hicn_protocol_t * h, + hicn_name_suffix_t * suffix) +{ + *suffix = ntohl (h->tcp.name_suffix); + return HICN_LIB_ERROR_NONE; +} + +int +tcp_set_data_name_suffix (hicn_type_t type, hicn_protocol_t * h, + const hicn_name_suffix_t * suffix) +{ + h->tcp.name_suffix = htonl (*suffix); + return HICN_LIB_ERROR_NONE; +} + +int +tcp_get_data_pathlabel (hicn_type_t type, const hicn_protocol_t * h, + u32 * pathlabel) +{ + *pathlabel = h->tcp.seq_ack; + return HICN_LIB_ERROR_NONE; +} + +int +tcp_set_data_pathlabel (hicn_type_t type, hicn_protocol_t * h, + const u32 pathlabel) +{ + h->tcp.seq_ack = pathlabel; + return HICN_LIB_ERROR_NONE; +} + +int +tcp_update_data_pathlabel (hicn_type_t type, hicn_protocol_t * h, + const hicn_faceid_t face_id) +{ + hicn_pathlabel_t pl = + (hicn_pathlabel_t) ((h->tcp.pathlabel & HICN_PATH_LABEL_MASK) >> (32 - + HICN_PATH_LABEL_SIZE)); + hicn_pathlabel_t new_pl; + + update_pathlabel (pl, face_id, &new_pl); + h->tcp.pathlabel = new_pl; + + return HICN_LIB_ERROR_NONE; +} + +int +tcp_reset_data_for_hash (hicn_type_t type, hicn_protocol_t * h) +{ + memset (&(h->tcp), 0, 4); + memset (&(h->tcp.seq_ack), 0, 12); + + return CHILD_OPS (reset_data_for_hash, type, h); +} + + +int +tcp_get_lifetime (hicn_type_t type, const hicn_protocol_t * h, + hicn_lifetime_t * lifetime) +{ + *lifetime = + ntohs (h->tcp.urg_ptr) << (h->tcp.data_offset_and_reserved & 0xF); + return HICN_LIB_ERROR_NONE; +} + +int +tcp_set_lifetime (hicn_type_t type, hicn_protocol_t * h, + const hicn_lifetime_t lifetime) +{ + u8 multiplier = 0; + u32 lifetime_scaled = lifetime; + + if (PREDICT_FALSE (lifetime >= HICN_MAX_LIFETIME)) + { + h->tcp.urg_ptr = htons (HICN_MAX_LIFETIME_SCALED); + h->tcp.data_offset_and_reserved = + (h-> + tcp.data_offset_and_reserved & ~0xF) | HICN_MAX_LIFETIME_MULTIPLIER; + return HICN_LIB_ERROR_NONE; + } + + while (lifetime_scaled > HICN_MAX_LIFETIME_SCALED + && multiplier <= HICN_MAX_LIFETIME_MULTIPLIER) + { + multiplier++; + lifetime_scaled = lifetime_scaled >> 1; + } + + h->tcp.urg_ptr = htons (lifetime_scaled); + h->tcp.data_offset_and_reserved = + (h->tcp.data_offset_and_reserved & ~0xF) | multiplier; + + return HICN_LIB_ERROR_NONE; +} + +int +tcp_update_checksums (hicn_type_t type, hicn_protocol_t * h, u16 partial_csum, + size_t payload_length) +{ + h->tcp.csum = 0; + + if (PREDICT_TRUE (partial_csum != 0)) + { + partial_csum = ~partial_csum; + } + + h->tcp.csum = csum (h, TCP_HDRLEN + payload_length, partial_csum); + + return CHILD_OPS (update_checksums, type, h, 0, payload_length); +} + +int +tcp_verify_checksums (hicn_type_t type, hicn_protocol_t * h, u16 partial_csum, + size_t payload_length) +{ + if (csum (h, TCP_HDRLEN + payload_length, ~partial_csum) != 0) + return HICN_LIB_ERROR_CORRUPTED_PACKET; + return CHILD_OPS (verify_checksums, type, h, 0, payload_length); +} + +#define TCP_OFFSET_MASK 13 +#define TCP_OFFSET_DATA_OFFSET 12 +#define TCP_OFFSET_IN_BITS_DATA_OFFSET 0 +#define TCP_OFFSET_IN_BITS_RESERVED 4 +#define TCP_OFFSET_IN_BITS_NS 7 + +#define TCP_DEFAULT_SRC_PORT 0x8000 +#define TCP_DEFAULT_DST_PORT 0x0080 +#define TCP_DEFAULT_WINDOW_SIZE 0 // In [2, 65535] +#define TCP_DEFAULT_DATA_OFFSET 5 // Size of the TCP header in words (= 4 bytes). Must be greater or equal than 5. +#define TCP_DEFAULT_CWR 0 +#define TCP_DEFAULT_ECE 0 +#define TCP_DEFAULT_URG 0 +#define TCP_DEFAULT_ACK 0 +#define TCP_DEFAULT_PSH 0 +#define TCP_DEFAULT_RST 0 +#define TCP_DEFAULT_SYN 1 +#define TCP_DEFAULT_FIN 0 + +int +tcp_rewrite_interest (hicn_type_t type, hicn_protocol_t * h, + const ip46_address_t * addr_new, + ip46_address_t * addr_old) +{ + u16 *tcp_checksum = &(h->tcp.csum); + + /* + * Padding fields are set to zero so we can apply checksum on the + * whole struct by interpreting it as IPv6 in all cases + * + * v4 code would be: + * csum = ip_csum_sub_even (*tcp_checksum, h->ipv4.saddr.as_u32); + * csum = ip_csum_add_even (csum, h->ipv4.saddr.as_u32); + */ + u16 csum = ip_csum_sub_even (*tcp_checksum, h->ipv6.saddr.as_u64[0]); + csum = ip_csum_sub_even (csum, h->ipv6.saddr.as_u64[1]); + csum = ip_csum_add_even (csum, h->ipv6.saddr.as_u64[0]); + csum = ip_csum_add_even (csum, h->ipv6.saddr.as_u64[1]); + + *tcp_checksum = ip_csum_fold (csum); + + return HICN_LIB_ERROR_NONE; +} + +int +tcp_rewrite_data (hicn_type_t type, hicn_protocol_t * h, + const ip46_address_t * addr_new, ip46_address_t * addr_old, + const hicn_faceid_t face_id) +{ + u16 *tcp_checksum = &(h->tcp.csum); + + /* + * Padding fields are set to zero so we can apply checksum on the + * whole struct by interpreting it as IPv6 in all cases + * + * v4 code would be: + * csum = ip_csum_sub_even (*tcp_checksum, h->ipv4.saddr.as_u32); + * csum = ip_csum_add_even (csum, h->ipv4.saddr.as_u32); + */ + u16 csum = ip_csum_sub_even (*tcp_checksum, addr_old->ip6.as_u64[0]); + csum = ip_csum_sub_even (*tcp_checksum, addr_old->ip6.as_u64[1]); + csum = ip_csum_add_even (csum, addr_new->ip6.as_u64[0]); + csum = ip_csum_add_even (csum, addr_new->ip6.as_u64[1]); + + csum = ip_csum_sub_even (csum, h->tcp.pathlabel); + tcp_update_data_pathlabel (type, h, face_id); + csum = ip_csum_add_even (csum, h->tcp.pathlabel); + + *tcp_checksum = ip_csum_fold (csum); + + return HICN_LIB_ERROR_NONE; +} + +int +tcp_get_current_header_length (hicn_type_t type, const hicn_protocol_t * h, + size_t * header_length) +{ + *header_length = TCP_HDRLEN; + return HICN_LIB_ERROR_NONE; +} + +int +tcp_get_header_length (hicn_type_t type, const hicn_protocol_t * h, + size_t * header_length) +{ + size_t child_header_length = 0; + int rc = CHILD_OPS (get_header_length, type, h, &child_header_length); + if (rc < 0) + return rc; + + *header_length = TCP_HDRLEN + child_header_length; + return HICN_LIB_ERROR_NONE; +} + +int +tcp_get_signature_size (hicn_type_t type, const hicn_protocol_t * h, + size_t * signature_size) +{ + return CHILD_OPS (get_signature_size, type, h, signature_size); +} + +int +tcp_set_signature_size (hicn_type_t type, hicn_protocol_t * h, + size_t signature_size) +{ + return CHILD_OPS (set_signature_size, type, h, signature_size); +} + +int +tcp_set_signature_timestamp(hicn_type_t type, hicn_protocol_t * h, + uint64_t signature_timestamp) +{ + return CHILD_OPS (set_signature_timestamp, type, h, signature_timestamp); +} + +int +tcp_get_signature_timestamp (hicn_type_t type, const hicn_protocol_t * h, + uint64_t * signature_timestamp) +{ + return CHILD_OPS (get_signature_timestamp, type, h, signature_timestamp); +} + +int +tcp_set_validation_algorithm (hicn_type_t type, hicn_protocol_t * h, + uint8_t validation_algorithm) +{ + return CHILD_OPS (set_validation_algorithm, type, h, validation_algorithm); +} + +int +tcp_get_validation_algorithm (hicn_type_t type, const hicn_protocol_t * h, + uint8_t * validation_algorithm) +{ + return CHILD_OPS (get_validation_algorithm, type, h, validation_algorithm); +} + +int +tcp_set_key_id (hicn_type_t type, hicn_protocol_t * h, + uint8_t *key_id) +{ + return CHILD_OPS (set_key_id, type, h, key_id); +} + +int +tcp_get_key_id (hicn_type_t type, hicn_protocol_t * h, + uint8_t **key_id, uint8_t *key_id_size) +{ + return CHILD_OPS (get_key_id, type, h, key_id, key_id_size); +} + +DECLARE_HICN_OPS (tcp); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/protocol/tcp.h b/lib/src/protocol/tcp.h new file mode 100755 index 000000000..68f4bf8f9 --- /dev/null +++ b/lib/src/protocol/tcp.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HICN_PROTOCOL_TCP_H +#define HICN_PROTOCOL_TCP_H + +#include "../base.h" +#include "../common.h" +#include "../name.h" + +/* + * NOTE: bitfields are problematic for portability reasons. There are provided + * here for reference and documentation purposes, we might just provide a macro + * to disable and use it instead of __BYTE_ORDER__. + */ +typedef struct __attribute__ ((packed)) +{ + u16 sport; + u16 dport; + union + { + u32 seq; + hicn_name_suffix_t name_suffix; + }; + union + { + u32 seq_ack; + struct + { + hicn_pathlabel_t pathlabel; + u8 pad[3]; + }; + }; + + union + { + struct + { + u8 data_offset_and_reserved; + u8 flags; + }; +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + struct + { + u16 reserved:4; + u16 doff:4; + u16 fin:1; + u16 syn:1; + u16 rst:1; + u16 psh:1; + u16 ack:1; + u16 urg:1; + u16 ece:1; + u16 cwr:1; + }; + struct + { /* __ denotes unchanged bitfields */ + u16 timescale:4; + u16 __doff:4; + u16 __fin:1; + u16 __syn:1; + u16 __rst:1; + u16 sig:1; + u16 __ack:1; + u16 man:1; + u16 id:1; + u16 __cwr:1; + }; +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + struct + { + u16 doff:4; + u16 reserved:4; + u16 cwr:1; + u16 ece:1; + u16 urg:1; + u16 ack:1; + u16 psh:1; + u16 rst:1; + u16 syn:1; + u16 fin:1; + }; + struct + { + u16 __doff:4; + u16 timescale:4; + u16 __cwr:1; + u16 id:1 u16 man:1; + u16 __ack:1; + u16 sig:1; + u16 __rst:1; + u16 __syn:1; + u16 __fin:1; + }; +#endif + }; + union + { + u16 window; + u16 ldr; + }; + u16 csum; + union + { + u16 urg_ptr; + u16 lifetime; + }; +} _tcp_header_t; + +#define TCP_HDRLEN sizeof(_tcp_header_t) + +#ifndef HICN_VPP_PLUGIN + +/* TCP flags bit 0 first. */ +#define foreach_tcp_flag \ + _ (FIN) /**< No more data from sender. */ \ + _ (SYN) /**< Synchronize sequence numbers. */ \ + _ (RST) /**< Reset the connection. */ \ + _ (PSH) /**< Push function. */ \ + _ (ACK) /**< Ack field significant. */ \ + _ (URG) /**< Urgent pointer field significant. */ \ + _ (ECE) /**< ECN-echo. Receiver got CE packet */ \ + _ (CWR) /**< Sender reduced congestion window */ + +enum +{ +#define _(f) TCP_FLAG_BIT_##f, + foreach_tcp_flag +#undef _ + TCP_N_FLAG_BITS, +}; + +enum +{ +#define _(f) TCP_FLAG_##f = 1 << TCP_FLAG_BIT_##f, + foreach_tcp_flag +#undef _ +}; + +#endif /* HICN_VPP_PLUGIN */ + +// get_data_name_suffix +// name->ip4.suffix = h->v4.tcp.seq; + + +#endif /* HICN_PROTOCOL_TCP_H */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/lib/src/protocol/udp.h b/lib/src/protocol/udp.h new file mode 100755 index 000000000..58cd65095 --- /dev/null +++ b/lib/src/protocol/udp.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HICN_PROTOCOL_UDP_H +#define HICN_PROTOCOL_UDP_H + +typedef struct +{ + u16 src_port; + u16 dst_port; + u16 length; + u16 checksum; +} _udp_header_t; + +#define UDP_HDRLEN sizeof(_udp_header_t) + +#endif /* HICN_PROTOCOL_UDP_H */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/libtransport/AUTHORS b/libtransport/AUTHORS new file mode 100755 index 000000000..12d1ae699 --- /dev/null +++ b/libtransport/AUTHORS @@ -0,0 +1,8 @@ +Libtransport authors are listed below + + Mauro Sardara + Michele Papalini + Alberto Compagno + Luca Muscariello + +Copyright (c) 2017-2019 Cisco and/or its affiliates. \ No newline at end of file diff --git a/libtransport/CMakeLists.txt b/libtransport/CMakeLists.txt new file mode 100755 index 000000000..f2ade4520 --- /dev/null +++ b/libtransport/CMakeLists.txt @@ -0,0 +1,122 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +project(Transport VERSION 1.0) + +set(CMAKE_MODULE_PATH + ${CMAKE_MODULE_PATH} + "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/Modules" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules" +) + +include(DefaultConfiguration) +include(Packager) +include(BuildMacros) + +if (NOT CMAKE_BUILD_TYPE) + message(STATUS "No build type selected, default to Release") + set(CMAKE_BUILD_TYPE "Release") +endif () + +set(TRANSPORT_ROOT_PATH "src/hicn/transport") + +set(TRANSPORT_CORE ${TRANSPORT_ROOT_PATH}/core) +set(TRANSPORT_TRANSPORT ${TRANSPORT_ROOT_PATH}/transport) +set(TRANSPORT_ERRORS ${TRANSPORT_ROOT_PATH}/errors) +set(TRANSPORT_UTILS ${TRANSPORT_ROOT_PATH}/utils) +set(TRANSPORT_HTTP ${TRANSPORT_ROOT_PATH}/http) +set(TRANSPORT_PORTABILITY ${TRANSPORT_ROOT_PATH}/portability) +set(TRANSPORT_INTERFACES ${TRANSPORT_ROOT_PATH}/interfaces) + +set(raaqm_config_path ${CMAKE_INSTALL_PREFIX}/etc/hicn-consumer.conf) + +# Install includes +set(INSTALL_INCLUDE_DIR include/hicn/transport) + +if ((BUILD_MEMIF_CONNECTOR OR BUILD_VPP_PLUGIN) AND "${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + set(__vpp__ 1) + find_package(Vpp REQUIRED) + find_package(Libmemif REQUIRED) + list(APPEND LIBTRANSPORT_INCLUDE_DIRS + ${VPP_INCLUDE_DIRS} + ${LIBMEMIF_INCLUDE_DIRS} + ) + + list(APPEND LIBRARIES + # ${VPP_INCLUDE_DIRS} + ${LIBMEMIF_LIBRARIES} + ) +endif () + +include(IosMacros) +find_package_wrapper(Libparc REQUIRED) +find_package_wrapper(Asio REQUIRED) + +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + find_package_wrapper(Libhicn REQUIRED) + if (__vpp__) + find_package_wrapper(HicnBinaryApi REQUIRED) + endif() + set(LIBTRANSPORT transport) +else() + set(HICN_LIBRARIES ${LIBHICN_SHARED}) + list(APPEND DEPENDENCIES + ${LIBHICN} + ${LIBHICN_SHARED} + ) + + if (__vpp__) + list(APPEND DEPENDENCIES + hicn_plugin + ) + endif() +endif() + +find_package(Threads REQUIRED) + +if (${COMPILE_TESTS}) + include(TestMacros) + find_package(GTest REQUIRED) + list(APPEND LIBTRANSPORT_INCLUDE_DIRS + ${GTEST_INCLUDE_DIRS} + ) +endif() + +list(APPEND LIBRARIES + ${LIBPARC_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + ${HICN_LIBRARIES} + ${VPP_LIBRARIES} + ${ANDROID_LIBRARIES} + ${OPENSSL_LIBRARIES} +) + +# Include dirs -- Order does matter! +list(APPEND LIBTRANSPORT_INCLUDE_DIRS + ${HICN_INCLUDE_DIRS} + ${HICN_BINARY_API_INCLUDE_DIRS} + ${LIBPARC_INCLUDE_DIRS} + ${CMAKE_THREADS_INCLUDE_DIRS} + ${ASIO_INCLUDE_DIRS} +) + +add_subdirectory(${TRANSPORT_ROOT_PATH}) + +# Packaging +add_package(libtransport + NAME "libtransport" + # DEPENDENCIES "fd.io" FIXME + DESCRIPTION "Shared Memory Interface" +) diff --git a/libtransport/README.md b/libtransport/README.md new file mode 100755 index 000000000..e7fc267ee --- /dev/null +++ b/libtransport/README.md @@ -0,0 +1,126 @@ +Libtransport: data transport library for hICN +==================================================== + +## Introduction ## + +This library provides transport services and socket API for applications willing to communicate +using the hICN protocol stack. + +Overview: + +- Implementation of the hICN core objects (interest, data, name..) exploiting the API provided by [libhicn](../lib). +- Connectors for connecting the application to either the hicn-plugin or the hicn-light forwarder. +- Transport protocols (RAAQM, CBR, RTP) +- Transport services (authentication, integrity, segmentation, reassembly, naming) +- Interfaces for Applications (from low-level interfaces for interest-data interaction to high level interfaces for Application Data Unit interaction) + +## Build Dependencies ## + +- libparc +- libmemif (linux only, if compiling with VPP support) +- libasio + +### Ubuntu 16.04 and Ubuntu 18.04 ### + +```bash + $ echo "deb [trusted=yes] https://nexus.fd.io/content/repositories/fd.io.master.ubuntu.$(lsb_release -sc).main/ ./" \ + | sudo tee -a /etc/apt/sources.list.d/99fd.io.list + $ sudo apt-get install libparc libasio-dev +``` + +If you wish to use the library for connecting to the vpp hicn-plugin, you will need to also install vpp, the vpp libraries and the libmemif libraries: + +- DEB packages: + - vpp + - vpp-lib + - vpp-dev + +You can get them either from from the vpp packages ot the source code. Check the [VPP wiki](https://wiki.fd.io/view/VPP) for instructions. + +Libmemif is in the vpp-lib and vpp-dev packages. + +### Mac OSX ### + +We recommend to use [HomeBrew](https://brew.sh/) for installing the libasio dependency: + +```bash + $ brew install asio +``` + +Download, compile and install libparc: + +```bash + $ git clone -b cframework/master https://gerrit.fd.io/r/cicn cframework && cd cframework + $ mkdir -p libparc.build && cd libparc.build + $ cmake ../libparc + $ make + $ make install +``` + +Libparc will be installed by default under `/usr/local/lib` and `/usr/local/include`. + +Since VPP does not support MAC OS, the hicn-plugin connector is not built. + +## Build The library ## + +From the project root folder: + +```bash + $ cd libtransport + $ mkdir build && cd build + $ cmake .. + $ make +``` +### Compile options ### + +The build process can be customized with the following options: + +- `CMAKE_INSTALL_PREFIX`: The path where you want to install the library. +- `CMAKE_BUILD_TYPE`: The build configuration. Options: `Release`, `Debug`. Default is `Release`. +- `ASIO_HOME`: The folder containing the libasio headers. +- `LIBPARC_HOME`: The folder containing the libparc headers and libraries. +- `VPP_HOME`: The folder containing the installation of VPP. +- `LIBMEMIF_HOME`: The folder containing the libmemif headers and libraries. +- `BUILD_MEMIF_CONNECTOR`: On linux, set this value to `ON` for building the VPP connector. + +An option can be set using cmake -D`OPTION`=`VALUE`. + +Install the library +------------------- + +For installing the library, from the cmake build folder: + +```bash + $ sudo make install +``` + +## Supported platforms + +- Ubuntu 16.04 LTS (x86_64) +- Ubuntu 18.04 LTS (x86_64) +- Debian Stable/Testing +- Red Hat Enterprise Linux 7 +- CentOS 7 +- Android 8 +- iOS 12 +- macOS 10.12 +- Windows 10 + +## License ## + +This software is distributed under the following license: + +``` +Copyright (c) 2017-2019 Cisco and/or its affiliates. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` \ No newline at end of file diff --git a/libtransport/cmake/Modules/Android.cmake b/libtransport/cmake/Modules/Android.cmake new file mode 100755 index 000000000..78918455a --- /dev/null +++ b/libtransport/cmake/Modules/Android.cmake @@ -0,0 +1,19 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +function (configure_android_environment) + set(CMAKE_CXX_FLAGS " -Wall -stdlib=libc++ -DASIO_STANDALONE -pthread -isystem -lm") + + #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" PARENT_SCOPE) + #set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ANDROID_C_FLAGS}" PARENT_SCOPE) +endfunction() \ No newline at end of file diff --git a/libtransport/cmake/Modules/DefaultConfiguration.cmake b/libtransport/cmake/Modules/DefaultConfiguration.cmake new file mode 100755 index 000000000..2110ac0fb --- /dev/null +++ b/libtransport/cmake/Modules/DefaultConfiguration.cmake @@ -0,0 +1,48 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# C/c++ standard +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_C_STANDARD 11) + +# Compilation options +option(BUILD_APPS "Build apps" ON) +option(BUILD_WITH_VPP "Add support for VPP" OFF) +option(COMPILE_TESTS "Compile functional tests" OFF) + +# Compilation flags + +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") +set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fpermissive") +set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -fpermissive") +set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -fpermissive") + +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DDEBUG") +set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") +set(CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL}") + +# Packaging +# TODO Libasio and libmemif +set(DEPS_DEB "libparc (>= 1.0)") +set(DEPS_RPM "libparc >= 1.0") +set(BUILD_DEPS_DEB "libtransport (>= 1.0), libhicn-dev (>= 1.0), libparc-dev (>= 1.0)") +set(BUILD_DEPS_RPM "libtransport >= 1.0, libhicn-devel >= 1.0, libparc-devel >= 1.0") + +set(VPP_DEPS_DEB "${DEPS_DEB}, hicn-plugin, vpp-lib (>= 18.07), vpp-dev (>= 18.07)") +set(VPP_DEPS_RPM "${DEPS_RPM}, hicn-plugin, vpp-lib >= 18.07), vpp-dev (>= 18.07)") +set(VPP_BUILD_DEPS_DEB "${BUILD_DEPS_DEB}, hicn-plugin, vpp-lib (>= 18.07), vpp-dev (>= 18.07)") +set(VPP_BUILD_DEPS_RPM "${BUILD_DEPS_RPM}, hicn-plugin, vpp-lib (>= 18.07), vpp-dev (>= 18.07)") + +set(PACKAGE_DESCRIPTION "This library is designed to provide a transport layer and API for applications willing to communicate using an hICN protocol stack.") +set(PACKAGE_HOMEPAGE "https://wiki.fd.io/view/Libicnet") \ No newline at end of file diff --git a/libtransport/cmake/Modules/Ios.cmake b/libtransport/cmake/Modules/Ios.cmake new file mode 100755 index 000000000..a4e625e98 --- /dev/null +++ b/libtransport/cmake/Modules/Ios.cmake @@ -0,0 +1,23 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +function (configure_ios_environment) + find_host_package ( OpenSSL REQUIRED ) + include_directories(extras/iOS) + + find_host_package(Libparc REQUIRED) + include_directories(${LIBPARC_INCLUDE_DIRS}) + + find_host_package(Libhicn REQUIRED) + include_directories(${HICN_INCLUDE_DIRS}) +endfunction() \ No newline at end of file diff --git a/libtransport/cmake/Modules/Packager.cmake b/libtransport/cmake/Modules/Packager.cmake new file mode 100755 index 000000000..019f18c4e --- /dev/null +++ b/libtransport/cmake/Modules/Packager.cmake @@ -0,0 +1,197 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generate DEB / RPM packages + +# Default variables + +if (NOT DEFINED ENV{VENDOR}) + set(VENDOR "Cisco Systems" CACHE STRING "Vendor") +else () + set(VENDOR ENV{VENDOR} CACHE STRING "Vendor") +endif () + +if (NOT DEFINED ENV{CONTACT}) + set(CONTACT "msardara@cisco.com" CACHE STRING "Contact") +else () + set(CONTACT ENV{CONTACT} CACHE STRING "Contact") +endif() + +if (NOT DEFINED ENV{PACKAGE_MAINTAINER}) + set(PACKAGE_MAINTAINER "Mauro Sardara (msardara@cisco.com)" CACHE STRING "Maintainer") +else () + set(PACKAGE_MAINTAINER ENV{PACKAGE_MAINTAINER} CACHE STRING "Maintainer") +endif() + +if (NOT DEFINED ENV{CPACK_PACKAGING_INSTALL_PREFIX}) + set(CPACK_PACKAGING_INSTALL_PREFIX "/usr") +else () + set(CPACK_PACKAGING_INSTALL_PREFIX ENV{CPACK_PACKAGING_INSTALL_PREFIX}) +endif() + +set(CPACK_COMPONENTS_ALL library headers utils documentation) + +function (make_package_internal PACKAGE_NAME PACKAGE_VERSION ARCHITECTURE GENERATOR TYPE DESCRIPTION HOMEPAGE) + set(CPACK_GENERATOR ${GENERATOR}) + set(CPACK_PACKAGE_VENDOR ${VENDOR}) + set(CPACK_PACKAGE_CONTACT ${CONTACT}) + + set(CPACK_${GENERATOR}_COMPONENT_INSTALL ON) + set(CPACK_${TYPE}_PACKAGE_MAINTAINER ${PACKAGE_MAINTAINER}) + set(CPACK_${TYPE}_PACKAGE_NAME ${PACKAGE_NAME}) + set(CPACK_${TYPE}_PACKAGE_VERSION ${PACKAGE_VERSION}) + set(CPACK_${TYPE}_PACKAGE_ARCHITECTURE ${ARCHITECTURE}) + set(CPACK_${TYPE}_PACKAGE_RELEASE 1) + set(CPACK_${TYPE}_PACKAGE_VENDOR ${VENDOR}) + set(CPACK_${TYPE}_PACKAGE_DESCRIPTION ${DESCRIPTION}) + set(CPACK_${TYPE}_PACKAGE_HOMEPAGE ${HOMEPAGE}) + + include(CPack) +endfunction() + +function(make_deb_package PACKAGE_NAME PACKAGE_VERSION ARCHITECTURE DEPS BUILD_DEPS DESCRIPTION HOMEPAGE) + + set(TYPE "DEBIAN") + set(GENERATOR "DEB") + + set(CPACK_${TYPE}_LIBRARY_PACKAGE_NAME "${PACKAGE_NAME}") + set(CPACK_${TYPE}_UTILS_PACKAGE_NAME "${PACKAGE_NAME}-utils") + set(CPACK_${TYPE}_HEADERS_PACKAGE_NAME "${PACKAGE_NAME}-dev") + set(CPACK_${TYPE}_DOCUMENTATION_PACKAGE_NAME "${PACKAGE_NAME}-doc") + + set(CPACK_${TYPE}_LIBRARY_FILE_NAME "${CPACK_${TYPE}_LIBRARY_PACKAGE_NAME}_${PACKAGE_VERSION}_${ARCHITECTURE}.deb") + set(CPACK_${TYPE}_UTILS_FILE_NAME "${CPACK_${TYPE}_UTILS_PACKAGE_NAME}_${PACKAGE_VERSION}_${ARCHITECTURE}.deb") + set(CPACK_${TYPE}_HEADERS_FILE_NAME "${CPACK_${TYPE}_HEADERS_PACKAGE_NAME}_${PACKAGE_VERSION}_${ARCHITECTURE}.deb") + set(CPACK_${TYPE}_DOCUMENTATION_FILE_NAME "${CPACK_${TYPE}_DOCUMENTATION_PACKAGE_NAME}_${PACKAGE_VERSION}_${ARCHITECTURE}.deb") + + set(CPACK_DEBIAN_LIBRARY_PACKAGE_SHLIBDEPS OFF) + + set(CPACK_${TYPE}_LIBRARY_PACKAGE_DEPENDS ${DEPS}) + set(CPACK_${TYPE}_UTILS_PACKAGE_DEPENDS ${CPACK_${TYPE}_LIBRARY_PACKAGE_NAME}) + set(CPACK_${TYPE}_HEADERS_PACKAGE_DEPENDS ${BUILD_DEPS}) + set(CPACK_${TYPE}_DOCUMENTATION_PACKAGE_DEPENDS "") + + make_package_internal(${PACKAGE_NAME} ${PACKAGE_VERSION} ${ARCHITECTURE} ${GENERATOR} ${TYPE} ${DESCRIPTION} ${HOMEPAGE}) +endfunction() + +function(make_rpm_package PACKAGE_NAME PACKAGE_VERSION ARCHITECTURE DEPS BUILD_DEPS DESCRIPTION HOMEPAGE) + set(TYPE "RPM") + set(GENERATOR "RPM") + + set(CPACK_${TYPE}_LIBRARY_PACKAGE_NAME "${PACKAGE_NAME}") + set(CPACK_${TYPE}_UTILS_PACKAGE_NAME "${PACKAGE_NAME}-utils") + set(CPACK_${TYPE}_HEADERS_PACKAGE_NAME "${PACKAGE_NAME}-devel") + set(CPACK_${TYPE}_DOCUMENTATION_PACKAGE_NAME "${PACKAGE_NAME}-doc") + + set(CPACK_${TYPE}_LIBRARY_FILE_NAME "${CPACK_${TYPE}_LIBRARY_PACKAGE_NAME}-${PACKAGE_VERSION}.${ARCHITECTURE}.rpm") + set(CPACK_${TYPE}_LIBRARY_FILE_NAME "${CPACK_${TYPE}_UTILS_PACKAGE_NAME}-${PACKAGE_VERSION}.${ARCHITECTURE}.rpm") + set(CPACK_${TYPE}_HEADERS_FILE_NAME "${CPACK_${TYPE}_HEADERS_PACKAGE_NAME}-${PACKAGE_VERSION}.${ARCHITECTURE}.rpm") + set(CPACK_${TYPE}_DOCUMENTATION_FILE_NAME "${CPACK_${TYPE}_DOCUMENTATION_PACKAGE_NAME}-${PACKAGE_VERSION}.${ARCHITECTURE}.rpm") + + set(CPACK_${TYPE}_LIBRARY_PACKAGE_AUTOREQ OFF) + + set(CPACK_${TYPE}_LIBRARY_PACKAGE_REQUIRES ${DEPS}) + set(CPACK_${TYPE}_UTILS_PACKAGE_DEPENDS ${CPACK_${TYPE}_LIBRARY_PACKAGE_NAME}) + set(CPACK_${TYPE}_HEADERS_PACKAGE_REQUIRES ${BUILD_DEPS}) + set(CPACK_${TYPE}_DOCUMENTATION_PACKAGE_REQUIRES "") + + set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION "/usr/etc" "/usr/lib/python2.7" "/usr/lib/python2.7/site-packages") + + make_package_internal(${PACKAGE_NAME} ${PACKAGE_VERSION} ${ARCHITECTURE} ${GENERATOR} ${TYPE} ${DESCRIPTION} ${HOMEPAGE}) +endfunction() + +function(make_tgz_package PACKAGE_NAME PACKAGE_VERSION ARCHITECTURE) + + set(TYPE "ARCHIVE") + set(GENERATOR "TGZ") + + set(CPACK_${TYPE}_COMPONENT_INSTALL ON) + set(CPACK_${TYPE}_LIBRARY_FILE_NAME "${PACKAGE_NAME}_${PACKAGE_VERSION}_${ARCHITECTURE}") + set(CPACK_${TYPE}_UTILS_FILE_NAME "${PACKAGE_NAME}-utils_${PACKAGE_VERSION}_${ARCHITECTURE}") + set(CPACK_${TYPE}_HEADERS_FILE_NAME "${PACKAGE_NAME}-dev_${PACKAGE_VERSION}_${ARCHITECTURE}") + set(CPACK_${TYPE}_DOCUMENTATION_FILE_NAME "${PACKAGE_NAME}-doc_${PACKAGE_VERSION}_${ARCHITECTURE}") + + set(CPACK_GENERATOR ${GENERATOR}) + set(CPACK_PACKAGE_VENDOR ${VENDOR}) + set(CPACK_PACKAGE_CONTACT ${CONTACT}) + + include(CPack) + +endfunction() + +function (make_package DEPS_DEB DEPS_RPM BUILD_DEPS_DEB BUILD_DEPS_RPM DESCRIPTION HOMEPAGE) + + if (NOT DEFINED ENV{PACKAGE_NAME}) + string(TOLOWER ${CMAKE_PROJECT_NAME} PACKAGE_NAME) + else () + string(TOLOWER $ENV{PACKAGE_NAME} PACKAGE_NAME) + endif () + + # Get the version + execute_process(COMMAND bash ${CMAKE_SOURCE_DIR}/scripts/version + OUTPUT_VARIABLE PACKAGE_VERSION) + + if (PACKAGE_VERSION) + string(STRIP ${PACKAGE_VERSION} PACKAGE_VERSION) + else () + set(PACKAGE_VERSION 1.0) + endif () + + if (EXISTS "/etc/lsb-release") + execute_process(COMMAND grep -oP "(?<=DISTRIB_ID=).*" /etc/lsb-release OUTPUT_VARIABLE DISTRIB_ID) + execute_process(COMMAND grep -oP "(?<=DISTRIB_RELEASE=).*" /etc/lsb-release OUTPUT_VARIABLE DISTRIB_RELEASE) + execute_process(COMMAND grep -oP "(?<=DISTRIB_CODENAME=).*" /etc/lsb-release OUTPUT_VARIABLE DISTRIB_CODENAME) + execute_process(COMMAND grep -oP "(?<=DISTRIB_DESCRIPTION=).*" /etc/lsb-release OUTPUT_VARIABLE DISTRIB_DESCRIPTION) + execute_process(COMMAND uname -m COMMAND tr -d '\n' OUTPUT_VARIABLE ARCHITECTURE) + + if (${ARCHITECTURE} STREQUAL "x86_64") + set(ARCHITECTURE "amd64") + endif() + + make_deb_package(${PACKAGE_NAME} + ${PACKAGE_VERSION} + ${ARCHITECTURE} + ${DEPS_DEB} + ${BUILD_DEPS_DEB} + ${DESCRIPTION} + ${HOMEPAGE}) + + elseif(EXISTS "/etc/redhat-release") + execute_process(COMMAND sudo yum install -y redhat-lsb) + execute_process(COMMAND lsb_release -si OUTPUT_VARIABLE DISTRIB_ID) + execute_process(COMMAND lsb_release -sr OUTPUT_VARIABLE DISTRIB_RELEASE) + execute_process(COMMAND lsb_release -sc OUTPUT_VARIABLE DISTRIB_CODENAME) + execute_process(COMMAND lsb_release -sd OUTPUT_VARIABLE DISTRIB_DESCRIPTION) + execute_process(COMMAND uname -m -m COMMAND tr -d '\n' OUTPUT_VARIABLE ARCHITECTURE) + + make_rpm_package(${PACKAGE_NAME} + ${PACKAGE_VERSION} + ${ARCHITECTURE} + ${DEPS_RPM} + ${BUILD_DEPS_RPM} + ${DESCRIPTION} + ${HOMEPAGE}) + else() + execute_process(COMMAND uname -m COMMAND tr -d '\n' OUTPUT_VARIABLE ARCHITECTURE) + + if (${ARCHITECTURE} STREQUAL "x86_64") + set(ARCHITECTURE "amd64") + endif() + + # Other linux system. Create a tar.gz package + make_tgz_package(${PACKAGE_NAME} + ${PACKAGE_VERSION} + ${ARCHITECTURE}) + + endif() +endfunction() diff --git a/libtransport/cmake/Modules/TestMacros.cmake b/libtransport/cmake/Modules/TestMacros.cmake new file mode 100755 index 000000000..680b5585f --- /dev/null +++ b/libtransport/cmake/Modules/TestMacros.cmake @@ -0,0 +1,15 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include(CTest) + diff --git a/libtransport/src/hicn/transport/CMakeLists.txt b/libtransport/src/hicn/transport/CMakeLists.txt new file mode 100755 index 000000000..f3c1cd2dc --- /dev/null +++ b/libtransport/src/hicn/transport/CMakeLists.txt @@ -0,0 +1,54 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +configure_file("config.h.in" "config.h" @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/config.h DESTINATION include/hicn/transport COMPONENT libtransport-dev) + +add_subdirectory(core) +add_subdirectory(errors) +add_subdirectory(http) +add_subdirectory(interfaces) +add_subdirectory(portability) +add_subdirectory(protocols) +add_subdirectory(utils) + +set (COMPILER_DEFINITIONS "-DASIO_STANDALONE") + +list(APPEND LIBTRANSPORT_INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/../.. + ${CMAKE_CURRENT_BINARY_DIR}/../.. +) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") +if (ANDROID_API) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -isystem -lm") +endif() + +build_library(${LIBTRANSPORT} + STATIC SHARED + SOURCES ${SOURCE_FILES} + INSTALL_HEADERS ${HEADER_FILES} + LINK_LIBRARIES ${LIBRARIES} + DEPENDS ${DEPENDENCIES} + COMPONENT libtransport + INCLUDE_DIRS ${LIBTRANSPORT_INCLUDE_DIRS} + INSTALL_ROOT_DIR hicn/transport + DEFINITIONS ${COMPILER_DEFINITIONS} +) + +if (${COMPILE_TESTS}) + add_subdirectory(core/test) + add_subdirectory(transport/test) +endif() \ No newline at end of file diff --git a/libtransport/src/hicn/transport/config.h.in b/libtransport/src/hicn/transport/config.h.in new file mode 100755 index 000000000..a140f4b78 --- /dev/null +++ b/libtransport/src/hicn/transport/config.h.in @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#cmakedefine TRANSPORT_HAVE_PTHREAD 1 + +#define RAAQM_CONFIG_PATH "@raaqm_config_path@" + +#cmakedefine __vpp__ diff --git a/libtransport/src/hicn/transport/core/CMakeLists.txt b/libtransport/src/hicn/transport/core/CMakeLists.txt new file mode 100755 index 000000000..c8dea8328 --- /dev/null +++ b/libtransport/src/hicn/transport/core/CMakeLists.txt @@ -0,0 +1,87 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/content_object.h + ${CMAKE_CURRENT_SOURCE_DIR}/facade.h + ${CMAKE_CURRENT_SOURCE_DIR}/interest.h + ${CMAKE_CURRENT_SOURCE_DIR}/key_locator.h + ${CMAKE_CURRENT_SOURCE_DIR}/key_locator_type.h + ${CMAKE_CURRENT_SOURCE_DIR}/socket_connector.h + ${CMAKE_CURRENT_SOURCE_DIR}/manifest.h + ${CMAKE_CURRENT_SOURCE_DIR}/manifest_inline.h + ${CMAKE_CURRENT_SOURCE_DIR}/manifest_format_fixed.h + ${CMAKE_CURRENT_SOURCE_DIR}/manifest_format.h + ${CMAKE_CURRENT_SOURCE_DIR}/name.h + ${CMAKE_CURRENT_SOURCE_DIR}/packet.h + ${CMAKE_CURRENT_SOURCE_DIR}/payload_type.h + ${CMAKE_CURRENT_SOURCE_DIR}/pending_interest.h + ${CMAKE_CURRENT_SOURCE_DIR}/portal.h + ${CMAKE_CURRENT_SOURCE_DIR}/prefix.h + ${CMAKE_CURRENT_SOURCE_DIR}/connector.h + ${CMAKE_CURRENT_SOURCE_DIR}/socket_connector.h + ${CMAKE_CURRENT_SOURCE_DIR}/forwarder_interface.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_forwarder_interface.h + ${CMAKE_CURRENT_SOURCE_DIR}/vpp_forwarder_interface.h + ${CMAKE_CURRENT_SOURCE_DIR}/memif_connector.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/content_object.cc + ${CMAKE_CURRENT_SOURCE_DIR}/interest.cc + ${CMAKE_CURRENT_SOURCE_DIR}/key_locator.cc + ${CMAKE_CURRENT_SOURCE_DIR}/socket_connector.cc + ${CMAKE_CURRENT_SOURCE_DIR}/pending_interest.cc + ${CMAKE_CURRENT_SOURCE_DIR}/packet.cc + ${CMAKE_CURRENT_SOURCE_DIR}/name.cc + ${CMAKE_CURRENT_SOURCE_DIR}/prefix.cc + ${CMAKE_CURRENT_SOURCE_DIR}/socket_connector.cc + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_forwarder_interface.cc + ${CMAKE_CURRENT_SOURCE_DIR}/manifest_format_fixed.cc + ${CMAKE_CURRENT_SOURCE_DIR}/connector.cc +) + +if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + if (BUILD_WITH_VPP OR BUILD_VPP_PLUGIN) + list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/vpp_forwarder_interface.h + ${CMAKE_CURRENT_SOURCE_DIR}/memif_connector.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_binary_api.h + ${CMAKE_CURRENT_SOURCE_DIR}/vpp_binary_api.h + ${CMAKE_CURRENT_SOURCE_DIR}/memif_binary_api.h + ) + + list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/vpp_forwarder_interface.cc + ${CMAKE_CURRENT_SOURCE_DIR}/memif_connector.cc + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_binary_api.c + ${CMAKE_CURRENT_SOURCE_DIR}/vpp_binary_api.c + ${CMAKE_CURRENT_SOURCE_DIR}/memif_binary_api.c + ) + endif() + + list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/raw_socket_connector.h + ${CMAKE_CURRENT_SOURCE_DIR}/raw_socket_interface.h + ) + + list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/raw_socket_connector.cc + ${CMAKE_CURRENT_SOURCE_DIR}/raw_socket_interface.cc + ) +endif() + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/connector.cc b/libtransport/src/hicn/transport/core/connector.cc new file mode 100755 index 000000000..ff567d78a --- /dev/null +++ b/libtransport/src/hicn/transport/core/connector.cc @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +namespace transport { + +namespace core { + +std::once_flag Connector::init_flag_; + +Connector::Connector() : packet_pool_() { init(); } + +void Connector::init() { increasePoolSize(); } + +void Connector::increasePoolSize(std::size_t size) { + // Allocate space for receiving packets + const auto capacity = packet_size * size; + uint8_t *buffer = static_cast(malloc(capacity)); + std::unique_ptr buffer0 = + utils::MemBuf::takeOwnership(buffer, capacity, 0, nullptr, nullptr, true); + + for (std::size_t i = 1; i < size; i++) { + auto b = buffer0->cloneOne(); + b->advance(i * packet_size); + packet_pool_.add(b.release()); + } +} + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/connector.h b/libtransport/src/hicn/transport/core/connector.h new file mode 100755 index 000000000..14201879c --- /dev/null +++ b/libtransport/src/hicn/transport/core/connector.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +namespace transport { + +namespace core { + +enum class ConnectorType : uint8_t { + SOCKET_CONNECTOR, + RAW_SOCKET_CONNECTOR, + VPP_CONNECTOR, +}; + +static constexpr std::size_t packet_size = 2000; +static constexpr std::size_t queue_size = 4096; +static constexpr std::size_t packet_pool_size = 4096; + +using PacketRing = utils::CircularFifo; +using PacketQueue = std::deque; +using PacketReceivedCallback = std::function; +using OnReconnect = std::function; +using PacketSentCallback = std::function; + +class Connector { + public: + Connector(); + + virtual ~Connector() = default; + + virtual void send(const Packet::MemBufPtr &packet) = 0; + + virtual void send(const uint8_t *packet, std::size_t len, + const PacketSentCallback &packet_sent = 0) = 0; + + virtual void close() = 0; + + virtual void enableBurst() = 0; + + virtual void state() = 0; + + protected: + void increasePoolSize(std::size_t size = packet_pool_size); + + TRANSPORT_ALWAYS_INLINE utils::ObjectPool::Ptr getPacket() { + auto result = packet_pool_.get(); + + while (TRANSPORT_EXPECT_FALSE(!result.first)) { + // Add packets to the pool + increasePoolSize(); + result = packet_pool_.get(); + } + + result.second->clear(); + return std::move(result.second); + } + + private: + void init(); + + protected: + static std::once_flag init_flag_; + utils::ObjectPool packet_pool_; + PacketQueue output_buffer_; +}; +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/content_object.cc b/libtransport/src/hicn/transport/core/content_object.cc new file mode 100755 index 000000000..dc2056582 --- /dev/null +++ b/libtransport/src/hicn/transport/core/content_object.cc @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +extern "C" { +TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat") +#include +} + +#include +#include + +namespace transport { + +namespace core { + +ContentObject::ContentObject(const Name &name, Packet::Format format) + : Packet(format) { + if (TRANSPORT_EXPECT_FALSE( + hicn_data_set_name(format, (hicn_header_t *)packet_start_, + name.getStructReference()) < 0)) { + throw errors::RuntimeException("Error filling the packet name."); + } + + if (TRANSPORT_EXPECT_FALSE( + hicn_data_get_name(format_, (hicn_header_t *)packet_start_, + name_.getStructReference()) < 0)) { + throw errors::MalformedPacketException(); + } +} + +ContentObject::ContentObject(hicn_format_t format) + : ContentObject(Packet::base_name, format) {} + +ContentObject::ContentObject(const Name &name, hicn_format_t format, + const uint8_t *payload, std::size_t size) + : ContentObject(name, format) { + appendPayload(payload, size); +} + +ContentObject::ContentObject(const uint8_t *buffer, std::size_t size) + : Packet(buffer, size) { + if (hicn_data_get_name(format_, (hicn_header_t *)packet_start_, + name_.getStructReference()) < 0) { + throw errors::RuntimeException("Error getting name from content object."); + } +} + +ContentObject::ContentObject(MemBufPtr &&buffer) : Packet(std::move(buffer)) { + if (hicn_data_get_name(format_, (hicn_header_t *)packet_start_, + name_.getStructReference()) < 0) { + throw errors::RuntimeException("Error getting name from content object."); + } +} + +ContentObject::ContentObject(ContentObject &&other) : Packet(std::move(other)) { + name_ = std::move(other.name_); + + if (hicn_data_get_name(format_, (hicn_header_t *)packet_start_, + name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } +} + +ContentObject::~ContentObject() {} + +const Name &ContentObject::getName() const { + if (!name_) { + if (hicn_data_get_name(format_, (hicn_header_t *)packet_start_, + (hicn_name_t *)name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } + } + + return name_; +} + +ContentObject &ContentObject::setName(const Name &name) { + if (hicn_data_set_name(format_, (hicn_header_t *)packet_start_, + name.getStructReference()) < 0) { + throw errors::RuntimeException("Error setting content object name."); + } + + if (hicn_data_get_name(format_, (hicn_header_t *)packet_start_, + name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } + + return *this; +} + +void ContentObject::setName(Name &&name) { + if (hicn_data_set_name(format_, (hicn_header_t *)packet_start_, + name.getStructReference()) < 0) { + throw errors::RuntimeException( + "Error getting the payload length from content object."); + } + + if (hicn_data_get_name(format_, (hicn_header_t *)packet_start_, + name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } +} + +uint32_t ContentObject::getPathLabel() const { + uint32_t path_label; + if (hicn_data_get_path_label((hicn_header_t *)packet_start_, &path_label) < + 0) { + throw errors::RuntimeException( + "Error retrieving the path label from content object"); + } + + return path_label; +} + +ContentObject &ContentObject::setPathLabel(uint32_t path_label) { + if (hicn_data_set_path_label((hicn_header_t *)packet_start_, path_label) < + 0) { + throw errors::RuntimeException( + "Error setting the path label from content object"); + } + + return *this; +} + +void ContentObject::setLocator(const ip_address_t &ip_address) { + if (hicn_data_set_locator(format_, (hicn_header_t *)packet_start_, + &ip_address) < 0) { + throw errors::RuntimeException("Error setting content object locator"); + } + + return; +} + +ip_address_t ContentObject::getLocator() const { + ip_address_t ip; + + if (hicn_data_get_locator(format_, (hicn_header_t *)packet_start_, &ip) < 0) { + throw errors::RuntimeException("Error getting content object locator."); + } + + return ip; +} + +void ContentObject::resetForHash() { + if (hicn_data_reset_for_hash( + format_, reinterpret_cast(packet_start_)) < 0) { + throw errors::RuntimeException( + "Error resetting content object fields for hash computation."); + } +} + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/content_object.h b/libtransport/src/hicn/transport/core/content_object.h new file mode 100755 index 000000000..c85259f20 --- /dev/null +++ b/libtransport/src/hicn/transport/core/content_object.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace transport { + +namespace core { + +// This class is used just to transfer buffer pointers +// without making a copy, as std::vector<> would do + +class ContentObject : public Packet { + public: + using Ptr = utils::ObjectPool::Ptr; + using HICNContentObject = hicn_header_t; + + ContentObject(Packet::Format format = HF_INET6_TCP); + + ContentObject(const Name &name, Packet::Format format = HF_INET6_TCP); + + ContentObject(const Name &name, hicn_format_t format, const uint8_t *payload, + std::size_t payload_size); + + ContentObject(const uint8_t *buffer, std::size_t size); + ContentObject(MemBufPtr &&buffer); + + ContentObject(const ContentObject &content_object) = delete; + + ContentObject(ContentObject &&content_object); + + ~ContentObject() override; + + const Name &getName() const; + + ContentObject &setName(const Name &name); + + void setName(Name &&name); + + uint32_t getPathLabel() const; + + ContentObject &setPathLabel(uint32_t path_label); + + void setLocator(const ip_address_t &ip_address) override; + + ip_address_t getLocator() const override; + + private: + void resetForHash() override; +}; + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/facade.h b/libtransport/src/hicn/transport/core/facade.h new file mode 100755 index 000000000..c28c84671 --- /dev/null +++ b/libtransport/src/hicn/transport/core/facade.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#ifdef __linux__ +#ifndef __ANDROID__ +#include +#ifdef __vpp__ +#include +#endif +#endif +#endif + +namespace transport { + +namespace core { + +using HicnForwarderPortal = Portal; + +#ifdef __linux__ +#ifndef __ANDROID_API__ +using RawSocketPortal = Portal; +#endif +#ifdef __vpp__ +using VPPForwarderPortal = Portal; +#endif +#endif + +using ContentObjectManifest = core::ManifestInline; +using InterestManifest = core::ManifestInline; + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/hicn/transport/core/forwarder_interface.h b/libtransport/src/hicn/transport/core/forwarder_interface.h new file mode 100755 index 000000000..e7b6fb1a6 --- /dev/null +++ b/libtransport/src/hicn/transport/core/forwarder_interface.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include + +namespace transport { + +namespace core { + +typedef struct { + uint64_t rx_packets; + uint64_t tx_packets; + uint64_t rx_bytes; + uint64_t tx_bytes; + uint64_t rx_errors; + uint64_t tx_errors; +} Counters; + +template +class ForwarderInterface { + static_assert(std::is_base_of::value, + "T must inherit from connector!"); + + static constexpr uint32_t standard_cs_reserved = 5000; + + protected: + ForwarderInterface(ConnectorType &c) + : connector_(c), + inet_address_({}), + inet6_address_({}), + mtu_(1500), + output_interface_(""), + content_store_reserved_(standard_cs_reserved) { + inet_address_.family = AF_INET; + inet6_address_.family = AF_INET6; + } + + public: + static constexpr uint8_t ack_code = 102; + + virtual ~ForwarderInterface() {} + + TRANSPORT_ALWAYS_INLINE void connect(bool is_consumer = true) { + static_cast(*this).connect(is_consumer); + } + + TRANSPORT_ALWAYS_INLINE void registerRoute(Prefix &prefix) { + static_cast(*this).registerRoute(); + } + + TRANSPORT_ALWAYS_INLINE std::uint32_t getMtu() { + return static_cast(*this).getMtu(); + } + + template < + typename R, + typename = std::enable_if_t< + std::is_base_of>::value, + R>> + TRANSPORT_ALWAYS_INLINE void send(R &&packet) { + counters_.tx_packets++; + counters_.tx_bytes += packet.payloadSize() + packet.headerSize(); + + if (_is_ipv4(packet.getFormat())) { + packet.setLocator(inet_address_); + } else { + packet.setLocator(inet6_address_); + } + + packet.setChecksum(); + connector_.send(packet.data()); + } + + template + TRANSPORT_ALWAYS_INLINE void send(const uint8_t *packet, std::size_t len, + Handler &&packet_sent) { + // ASIO_COMPLETION_HANDLER_CHECK(Handler, packet_sent) type_check; + counters_.tx_packets++; + counters_.tx_bytes += len; + + // Perfect forwarding + connector_.send(packet, len, std::forward(packet_sent)); + } + + TRANSPORT_ALWAYS_INLINE void shutdown() { connector_.close(); } + + TRANSPORT_ALWAYS_INLINE Connector &getConnector() { return connector_; } + + TRANSPORT_ALWAYS_INLINE void setContentStoreSize(uint32_t cs_size) { + content_store_reserved_ = cs_size; + } + + TRANSPORT_ALWAYS_INLINE uint32_t getContentStoreSize() const { + return content_store_reserved_; + } + + TRANSPORT_ALWAYS_INLINE void setOutputInterface( + const std::string &interface) { + output_interface_ = interface; + } + + TRANSPORT_ALWAYS_INLINE std::string &getOutputInterface() { + return output_interface_; + } + + protected: + ConnectorType &connector_; + ip_address_t inet_address_; + ip_address_t inet6_address_; + uint16_t mtu_; + std::string output_interface_; + uint32_t content_store_reserved_; + Counters counters_; +}; + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/hicn/transport/core/hicn_binary_api.c b/libtransport/src/hicn/transport/core/hicn_binary_api.c new file mode 100755 index 000000000..c49cb5c88 --- /dev/null +++ b/libtransport/src/hicn/transport/core/hicn_binary_api.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#ifdef __vpp__ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +// uword unformat_sw_if_index (unformat_input_t * input, va_list * args); + +/* Declare message IDs */ +#include + +#define vl_endianfun /* define message structures */ +#define vl_print(handle, ...) +#define vl_printfun +#define vl_api_version(n, v) static u32 api_version = (v); +#define vl_msg_name_crc_list +#include +#undef vl_msg_name_crc_list +#undef vl_api_version +#undef vl_printfun +#undef vl_endianfun + +///////////////////////////////////////////////////// +const char *HICN_ERROR_STRING[] = { +#define _(a, b, c) c, + foreach_hicn_error +#undef _ +}; +///////////////////////////////////////////////////// + +#define POINTER_MAP_SIZE 32 +static void *global_pointers_map[POINTER_MAP_SIZE]; +static uint8_t global_pointers_map_index = 0; + +#define CONTEXT_SAVE(pointer, mp) \ + do { \ + global_pointers_map[global_pointers_map_index] = pointer; \ + mp->context = global_pointers_map_index++; \ + global_pointers_map_index %= POINTER_MAP_SIZE; \ + } while (0); + +#define CONTEXT_GET(mp, pointer) \ + do { \ + pointer = global_pointers_map[mp->context]; \ + } while (0); + +/* + * Table of message reply handlers, must include boilerplate handlers + * we just generated + */ +#define foreach_hicn_api_reply_msg \ + _(HICN_API_REGISTER_PROD_APP_REPLY, hicn_api_register_prod_app_reply) \ + _(HICN_API_REGISTER_CONS_APP_REPLY, hicn_api_register_cons_app_reply) \ + _(HICN_API_ROUTE_NHOPS_ADD_REPLY, hicn_api_route_nhops_add_reply) + +int hicn_binary_api_register_prod_app( + vpp_plugin_binary_api_t *api, hicn_producer_input_params *input_params, + hicn_producer_output_params *output_params) { + vl_api_hicn_api_register_prod_app_t *mp; + vpp_plugin_binary_api_t *hm = api; + api->vpp_api->user_param = output_params; + + /* Construct the API message */ + M(HICN_API_REGISTER_PROD_APP, mp); + + CONTEXT_SAVE(api, mp) + + mp->len = (u8)input_params->prefix.prefix_length; + mp->swif = clib_host_to_net_u32(input_params->swif); + mp->cs_reserved = clib_host_to_net_u32(input_params->cs_reserved); + + mp->prefix[0] = clib_host_to_net_u64(input_params->prefix.ip6.as_u64[0]); + mp->prefix[1] = clib_host_to_net_u64(input_params->prefix.ip6.as_u64[1]); + + TRANSPORT_LOGI("Prefix length: %u", mp->len); + TRANSPORT_LOGI("Memif ID: %u", mp->swif); + + return vpp_binary_api_send_request_wait_reply(api->vpp_api, mp); +} + +static void vl_api_hicn_api_register_prod_app_reply_t_handler( + vl_api_hicn_api_register_prod_app_reply_t *mp) { + vpp_plugin_binary_api_t *binary_api; + CONTEXT_GET(mp, binary_api); + hicn_producer_output_params *params = binary_api->vpp_api->user_param; + + binary_api->vpp_api->ret_val = clib_net_to_host_u32(mp->retval); + params->cs_reserved = mp->cs_reserved; + params->prod_addr.ip6.as_u64[0] = mp->prod_addr[0]; + params->prod_addr.ip6.as_u64[1] = mp->prod_addr[1]; + params->face_id = clib_net_to_host_u32(mp->faceid); + + TRANSPORT_LOGI("ret :%s", get_error_string(binary_api->vpp_api->ret_val)); + + vpp_binary_api_unlock_waiting_thread(binary_api->vpp_api); +} + +int hicn_binary_api_register_cons_app( + vpp_plugin_binary_api_t *api, hicn_consumer_input_params *input_params, + hicn_consumer_output_params *output_params) { + vl_api_hicn_api_register_cons_app_t *mp; + vpp_plugin_binary_api_t *hm = api; + + hm->vpp_api->user_param = output_params; + + /* Construct the API message */ + M(HICN_API_REGISTER_CONS_APP, mp); + + mp->swif = clib_host_to_net_u32(input_params->swif); + + CONTEXT_SAVE(api, mp) + + TRANSPORT_LOGI("Message created"); + + return vpp_binary_api_send_request_wait_reply(api->vpp_api, mp); +} + +static void vl_api_hicn_api_register_cons_app_reply_t_handler( + vl_api_hicn_api_register_cons_app_reply_t *mp) { + vpp_plugin_binary_api_t *binary_api; + CONTEXT_GET(mp, binary_api); + hicn_consumer_output_params *params = binary_api->vpp_api->user_param; + + binary_api->vpp_api->ret_val = clib_net_to_host_u32(mp->retval); + + params->src4.ip4.as_u32 = clib_net_to_host_u32(mp->src_addr4); + params->src6.ip6.as_u64[0] = clib_net_to_host_u64(mp->src_addr6[0]); + params->src6.ip6.as_u64[1] = clib_net_to_host_u64(mp->src_addr6[1]); + params->face_id = clib_host_to_net_u32(mp->faceid); + + vpp_binary_api_unlock_waiting_thread(binary_api->vpp_api); +} + +int hicn_binary_api_register_route( + vpp_plugin_binary_api_t *api, + hicn_producer_set_route_params *input_params) { + vl_api_hicn_api_route_nhops_add_t *mp; + vpp_plugin_binary_api_t *hm = api; + + /* Construct the API message */ + M(HICN_API_ROUTE_NHOPS_ADD, mp); + + CONTEXT_SAVE(api, mp) + + mp->prefix[0] = input_params->prefix.ip6.as_u64[0]; + mp->prefix[1] = input_params->prefix.ip6.as_u64[1]; + mp->len = input_params->prefix.prefix_length; + mp->face_ids[0] = input_params->face_id; + mp->n_faces = 1; + + return vpp_binary_api_send_request_wait_reply(api->vpp_api, mp); +} + +static void vl_api_hicn_api_route_nhops_add_reply_t_handler( + vl_api_hicn_api_route_nhops_add_reply_t *mp) { + vpp_plugin_binary_api_t *binary_api; + CONTEXT_GET(mp, binary_api); + + binary_api->vpp_api->ret_val = clib_net_to_host_u32(mp->retval); + + vpp_binary_api_unlock_waiting_thread(binary_api->vpp_api); +} + +static int hicn_binary_api_setup_handlers(vpp_plugin_binary_api_t *binary_api) { + vpp_plugin_binary_api_t *sm __attribute__((unused)) = binary_api; +#define _(N, n) \ + vl_msg_api_set_handlers(VL_API_##N + sm->msg_id_base, #n, \ + vl_api_##n##_t_handler, vl_noop_handler, \ + vl_api_##n##_t_endian, vl_api_##n##_t_print, \ + sizeof(vl_api_##n##_t), 1); + foreach_hicn_api_reply_msg; +#undef _ + return 0; +} + +char *hicn_binary_api_get_error_string(int ret_val) { + return get_error_string(ret_val); +} + +vpp_plugin_binary_api_t *hicn_binary_api_init(vpp_binary_api_t *api) { + vpp_plugin_binary_api_t *ret = malloc(sizeof(vpp_plugin_binary_api_t)); + u8 *name = format(0, "hicn_%08x%c", api_version, 0); + ret->msg_id_base = vl_client_get_first_plugin_msg_id((char *)name); + ret->vpp_api = api; + ret->my_client_index = api->my_client_index; + hicn_binary_api_setup_handlers(ret); + return ret; +} + +#endif // __vpp__ diff --git a/libtransport/src/hicn/transport/core/hicn_binary_api.h b/libtransport/src/hicn/transport/core/hicn_binary_api.h new file mode 100755 index 000000000..752844153 --- /dev/null +++ b/libtransport/src/hicn/transport/core/hicn_binary_api.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#ifdef __vpp__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "stdint.h" + +typedef union { + uint8_t data[4]; + uint32_t data_u32; + /* Aliases. */ + uint8_t as_u8[4]; + uint16_t as_u16[2]; + uint32_t as_u32; +} ip4_address; + +typedef union { + uint8_t as_u8[16]; + uint16_t as_u16[8]; + uint32_t as_u32[4]; + uint64_t as_u64[2]; +} ip6_address; + +typedef enum { IP_TYPE_ANY, IP_TYPE_IP4, IP_TYPE_IP6 } ip46_type; + +typedef struct { + ip46_type type; + uint8_t prefix_length; + union { + ip4_address ip4; + ip6_address ip6; + }; +} ip46_address; + +typedef struct { + ip46_address prefix; + uint32_t swif; + uint32_t cs_reserved; +} hicn_producer_input_params; + +typedef struct { + uint32_t swif; +} hicn_consumer_input_params; + +typedef struct { + uint32_t cs_reserved; + ip46_address prod_addr; + uint32_t face_id; +} hicn_producer_output_params; + +typedef struct { + ip46_address src4; + ip46_address src6; + uint32_t face_id; +} hicn_consumer_output_params; + +typedef struct { + ip46_address prefix; + uint32_t face_id; +} hicn_producer_set_route_params; + +vpp_plugin_binary_api_t* hicn_binary_api_init(vpp_binary_api_t* api); + +int hicn_binary_api_register_prod_app( + vpp_plugin_binary_api_t* api, hicn_producer_input_params* input_params, + hicn_producer_output_params* output_params); + +int hicn_binary_api_register_cons_app( + vpp_plugin_binary_api_t* api, hicn_consumer_input_params* input_params, + hicn_consumer_output_params* output_params); + +int hicn_binary_api_register_route( + vpp_plugin_binary_api_t* api, hicn_producer_set_route_params* input_params); + +char* hicn_binary_api_get_error_string(int ret_val); + +#ifdef __cplusplus +} +#endif + +#endif // __vpp__ \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/hicn_forwarder_interface.cc b/libtransport/src/hicn/transport/core/hicn_forwarder_interface.cc new file mode 100755 index 000000000..03a294957 --- /dev/null +++ b/libtransport/src/hicn/transport/core/hicn_forwarder_interface.cc @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#define ADDR_INET 1 +#define ADDR_INET6 2 +#define ADD_ROUTE 3 +#define REQUEST_LIGHT 100 + +union AddressLight { + uint32_t ipv4; + struct in6_addr ipv6; +}; + +typedef struct { + uint8_t message_type; + uint8_t command_id; + uint16_t length; + uint32_t seq_num; + char symbolic_or_connid[16]; + union AddressLight address; + uint16_t cost; + uint8_t address_type; + uint8_t len; +} RouteToSelfCommand; + +namespace transport { + +namespace core { + +HicnForwarderInterface::HicnForwarderInterface(SocketConnector &connector) + : ForwarderInterface(connector) {} + +HicnForwarderInterface::~HicnForwarderInterface() {} + +void HicnForwarderInterface::connect(bool is_consumer) { connector_.connect(); } + +void HicnForwarderInterface::registerRoute(Prefix &prefix) { + auto addr = prefix.toSockaddr(); + const char *identifier = {"SELF_ROUTE"}; + + // allocate command payload + RouteToSelfCommand *route_to_self = new RouteToSelfCommand(); + + // check and set IP address + if (addr->sa_family == AF_INET) { + route_to_self->address_type = ADDR_INET; + route_to_self->address.ipv4 = ((Sockaddr4 *)addr.get())->sin_addr.s_addr; + } else if (addr->sa_family == AF_INET6) { + route_to_self->address_type = ADDR_INET6; + route_to_self->address.ipv6 = ((Sockaddr6 *)addr.get())->sin6_addr; + } + + // Fill remaining payload fields + strcpy(route_to_self->symbolic_or_connid, identifier); + route_to_self->cost = 1; + route_to_self->len = prefix.getPrefixLength(); + + // Allocate and fill the header + route_to_self->command_id = ADD_ROUTE; + route_to_self->message_type = REQUEST_LIGHT; + route_to_self->length = 1; + // route_to_self->seq_num not needed for now + + send((uint8_t *)route_to_self, sizeof(RouteToSelfCommand), + [route_to_self]() { delete route_to_self; }); +} + +} // namespace core + +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/hicn_forwarder_interface.h b/libtransport/src/hicn/transport/core/hicn_forwarder_interface.h new file mode 100755 index 000000000..e57fae105 --- /dev/null +++ b/libtransport/src/hicn/transport/core/hicn_forwarder_interface.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include + +namespace transport { + +namespace core { + +class HicnForwarderInterface + : public ForwarderInterface { + public: + union addressLight { + uint32_t ipv4; + struct in6_addr ipv6; + }; + + struct route_to_self_command { + uint8_t messageType; + uint8_t commandID; + uint16_t length; + uint32_t seqNum; + char symbolicOrConnid[16]; + union addressLight address; + uint16_t cost; + uint8_t addressType; + uint8_t len; + }; + + using route_to_self_command = struct route_to_self_command; + using ConnectorType = SocketConnector; + + HicnForwarderInterface(SocketConnector &connector); + + ~HicnForwarderInterface(); + + void connect(bool is_consumer); + + void registerRoute(Prefix &prefix); + + std::uint16_t getMtu() { return interface_mtu; } + + private: + static constexpr std::uint16_t interface_mtu = 1500; +}; + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/hicn/transport/core/hicn_memif_api.c b/libtransport/src/hicn/transport/core/hicn_memif_api.c new file mode 100755 index 000000000..e69de29bb diff --git a/libtransport/src/hicn/transport/core/interest.cc b/libtransport/src/hicn/transport/core/interest.cc new file mode 100755 index 000000000..ff4a5bb34 --- /dev/null +++ b/libtransport/src/hicn/transport/core/interest.cc @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +extern "C" { +TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat") +#include +} + +#include +#include + +namespace transport { + +namespace core { + +Interest::Interest(const Name &interest_name, Packet::Format format) + : Packet(format) { + if (hicn_interest_set_name(format_, (hicn_header_t *)packet_start_, + interest_name.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } + + if (hicn_interest_get_name(format_, (hicn_header_t *)packet_start_, + name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } +} + +Interest::Interest(hicn_format_t format) : Interest(base_name, format) {} + +Interest::Interest(const uint8_t *buffer, std::size_t size) + : Packet(buffer, size) { + if (hicn_interest_get_name(format_, (hicn_header_t *)packet_start_, + name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } +} + +Interest::Interest(MemBufPtr &&buffer) : Packet(std::move(buffer)) { + if (hicn_interest_get_name(format_, (hicn_header_t *)packet_start_, + name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } +} + +Interest::Interest(Interest &&other_interest) + : Packet(std::move(other_interest)) { + name_ = std::move(other_interest.name_); +} + +Interest::~Interest() {} + +const Name &Interest::getName() const { + if (!name_) { + if (hicn_interest_get_name(format_, (hicn_header_t *)packet_start_, + (hicn_name_t *)name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } + } + + return name_; +} + +Name &Interest::getWritableName() { + if (!name_) { + if (hicn_interest_get_name(format_, (hicn_header_t *)packet_start_, + (hicn_name_t *)name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } + } + + return name_; +} + +Interest &Interest::setName(const Name &name) { + if (hicn_interest_set_name(format_, (hicn_header_t *)packet_start_, + name.getStructReference()) < 0) { + throw errors::RuntimeException("Error setting interest name."); + } + + if (hicn_interest_get_name(format_, (hicn_header_t *)packet_start_, + name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } + + return *this; +} + +Interest &Interest::setName(Name &&name) { + if (hicn_interest_set_name(format_, (hicn_header_t *)packet_start_, + name.getStructReference()) < 0) { + throw errors::RuntimeException("Error setting interest name."); + } + + if (hicn_interest_get_name(format_, (hicn_header_t *)packet_start_, + name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } + + return *this; +} + +void Interest::setLocator(const ip_address_t &ip_address) { + if (hicn_interest_set_locator(format_, (hicn_header_t *)packet_start_, + &ip_address) < 0) { + throw errors::RuntimeException("Error setting interest locator."); + } + + return; +} + +ip_address_t Interest::getLocator() const { + ip_address_t ip; + + if (hicn_interest_get_locator(format_, (hicn_header_t *)packet_start_, &ip) < + 0) { + throw errors::RuntimeException("Error getting interest locator."); + } + + return ip; +} + +void Interest::resetForHash() { + if (hicn_interest_reset_for_hash( + format_, reinterpret_cast(packet_start_)) < 0) { + throw errors::RuntimeException( + "Error resetting interest fields for hash computation."); + } +} + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/interest.h b/libtransport/src/hicn/transport/core/interest.h new file mode 100755 index 000000000..75fcba8eb --- /dev/null +++ b/libtransport/src/hicn/transport/core/interest.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +namespace transport { + +namespace core { + +class Interest + : public Packet /*, public std::enable_shared_from_this*/ { + public: + using Ptr = utils::ObjectPool::Ptr; + + Interest(Packet::Format format = HF_INET6_TCP); + + Interest(const Name &interest_name, Packet::Format format = HF_INET6_TCP); + + Interest(const uint8_t *buffer, std::size_t size); + Interest(MemBufPtr &&buffer); + + /* + * Enforce zero-copy. + */ + Interest(const Interest &other_interest) = delete; + Interest &operator=(const Interest &other_interest) = delete; + + Interest(Interest &&other_interest); + + ~Interest() override; + + const Name &getName() const; + + Name &getWritableName(); + + Interest &setName(const Name &name); + + Interest &setName(Name &&name); + + void setLocator(const ip_address_t &ip_address) override; + + ip_address_t getLocator() const override; + + private: + void resetForHash() override; +}; + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/key_locator.cc b/libtransport/src/hicn/transport/core/key_locator.cc new file mode 100755 index 000000000..509fc35ff --- /dev/null +++ b/libtransport/src/hicn/transport/core/key_locator.cc @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +namespace transport { + +namespace core { + +KeyLocator::KeyLocator() : type_(KeyLocatorType::UNKNOWN) {} + +KeyLocator::KeyLocator(KeyLocatorType type, Name &name) + : type_(type), name_(name) {} + +Name &KeyLocator::getName() { return name_; } + +void KeyLocator::setName(Name &name) { name_ = name; } + +void KeyLocator::setType(KeyLocatorType type) { type_ = type; } + +KeyLocatorType KeyLocator::getType() { return type_; } + +void KeyLocator::clear() { + type_ = KeyLocatorType::UNKNOWN; + name_.clear(); +} + +} // end namespace core + +} // end namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/key_locator.h b/libtransport/src/hicn/transport/core/key_locator.h new file mode 100755 index 000000000..ae3a4ab08 --- /dev/null +++ b/libtransport/src/hicn/transport/core/key_locator.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace transport { + +namespace core { + +class KeyLocator : public std::enable_shared_from_this { + public: + KeyLocator(); + + KeyLocator(KeyLocatorType type, Name &name); + + KeyLocatorType getType(); + + void setType(KeyLocatorType type); + + void setName(Name &name); + + Name &getName(); + + void clear(); + + private: + KeyLocatorType type_; + Name name_; +}; + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/key_locator_type.h b/libtransport/src/hicn/transport/core/key_locator_type.h new file mode 100755 index 000000000..0c84a43ca --- /dev/null +++ b/libtransport/src/hicn/transport/core/key_locator_type.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +namespace transport { + +namespace core { + +enum Type { NAME = 0, KEY_DIGEST = 1, UNKNOWN = 255 }; + +typedef enum Type KeyLocatorType; + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/manifest.cc b/libtransport/src/hicn/transport/core/manifest.cc new file mode 100755 index 000000000..3f890f3d0 --- /dev/null +++ b/libtransport/src/hicn/transport/core/manifest.cc @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +namespace transport { + +namespace core { + +std::string ManifestEncoding::manifest_type = std::string("manifest_type"); + +std::map ManifestEncoding::manifest_types = { + {FINAL_CHUNK_NUMBER, "FinalChunkNumber"}, {NAME_LIST, "NameList"}}; + +std::string ManifestEncoding::final_chunk_number = + std::string("final_chunk_number"); +std::string ManifestEncoding::content_name = std::string("content_name"); + +} // end namespace core + +} // end namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/manifest.h b/libtransport/src/hicn/transport/core/manifest.h new file mode 100755 index 000000000..767addb2e --- /dev/null +++ b/libtransport/src/hicn/transport/core/manifest.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include + +namespace transport { + +namespace core { + +using typename core::Name; +using typename core::Packet; +using typename core::PayloadType; + +template +class Manifest : public Base { + static_assert(std::is_base_of::value, + "Base must inherit from packet!"); + + public: + using Encoder = typename FormatTraits::Encoder; + using Decoder = typename FormatTraits::Decoder; + + Manifest() + : packet_(new Base(HF_INET6_TCP_AH), nullptr), + encoder_(*packet_), + decoder_(*packet_) { + Base::setPayloadType(PayloadType::MANIFEST); + } + + Manifest(const core::Name& name) + : packet_(new Base(name, HF_INET6_TCP_AH), nullptr), + encoder_(*packet_), + decoder_(*packet_) { + Base::setPayloadType(PayloadType::MANIFEST); + } + + Manifest(typename Base::Ptr&& base) + : packet_(std::move(base)), encoder_(*packet_), decoder_(*packet_) { + Base::setPayloadType(PayloadType::MANIFEST); + } + + template + Manifest(T&& base) + : packet_(new Base(std::move(base)), nullptr), + encoder_(*packet_), + decoder_(*packet_) { + Base::setPayloadType(PayloadType::MANIFEST); + } + + virtual ~Manifest() = default; + + bool operator==(const Manifest& other) { + return this->packet_ == other.packet_; + } + + std::size_t estimateManifestSize(std::size_t additional_entries = 0) { + return static_cast(*this).estimateManifestSizeImpl( + additional_entries); + } + + /* + * After the call to encode, users MUST call clear before adding data + * to the manifest. + */ + Manifest& encode() { return static_cast(*this).encodeImpl(); } + + Manifest& decode() { + Manifest::decoder_.decode(); + + manifest_type_ = decoder_.getManifestType(); + hash_algorithm_ = decoder_.getHashAlgorithm(); + is_last_ = decoder_.getIsFinalManifest(); + + return static_cast(*this).decodeImpl(); + } + + static std::size_t getManifestHeaderSize() { + return Encoder::getManifestHeaderSize(); + } + + Manifest& setManifestType(ManifestType type) { + manifest_type_ = type; + encoder_.setManifestType(manifest_type_); + return *this; + } + + Manifest& setHashAlgorithm(HashAlgorithm hash_algorithm) { + hash_algorithm_ = hash_algorithm; + encoder_.setHashAlgorithm(hash_algorithm_); + return *this; + } + + HashAlgorithm getHashAlgorithm() { return hash_algorithm_; } + + ManifestType getManifestType() const { return manifest_type_; } + + bool isFinalManifest() const { return is_last_; } + + Manifest& setVersion(ManifestVersion version) { + encoder_.setVersion(version); + return *this; + } + + Manifest& setFinalBlockNumber(std::uint32_t final_block_number) { + encoder_.setFinalBlockNumber(final_block_number); + return *this; + } + + uint32_t getFinalBlockNumber() const { + return decoder_.getFinalBlockNumber(); + } + + ManifestVersion getVersion() const { return decoder_.getVersion(); } + + Manifest& setFinalManifest(bool is_final_manifest) { + encoder_.setIsFinalManifest(is_final_manifest); + is_last_ = is_final_manifest; + return *this; + } + + Manifest& clear() { + encoder_.clear(); + decoder_.clear(); + return *this; + } + + void setSignatureSize(std::size_t size_bits) { + Packet::setSignatureSize(size_bits); + encoder_.update(); + } + + typename Base::Ptr&& getPacket() { return std::move(packet_); } + + protected: + typename Base::Ptr packet_; + ManifestType manifest_type_; + HashAlgorithm hash_algorithm_; + bool is_last_; + + Encoder encoder_; + Decoder decoder_; +}; + +} // end namespace core + +} // end namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/manifest_format.h b/libtransport/src/hicn/transport/core/manifest_format.h new file mode 100755 index 000000000..1dcf013dc --- /dev/null +++ b/libtransport/src/hicn/transport/core/manifest_format.h @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include +#include +#include + +namespace transport { + +namespace core { + +enum class ManifestFields : uint8_t { + VERSION, + HASH_ALGORITHM, + SEGMENT_CALCULATION_STRATEGY, + FINAL_MANIFEST, + NAME_HASH_LIST, + BASE_NAME +}; + +enum class ManifestVersion : uint8_t { + VERSION_1 = 1, +}; + +enum class ManifestType : uint8_t { + INLINE_MANIFEST = 1, + FINAL_CHUNK_NUMBER = 2, + FLIC_MANIFEST = 3, +}; + +enum class HashAlgorithm : uint8_t { + SHA_256 = static_cast(utils::CryptoHashType::SHA_256), + SHA_512 = static_cast(utils::CryptoHashType::SHA_512), + CRC32C = static_cast(utils::CryptoHashType::CRC32C), +}; + +enum class NextSegmentCalculationStrategy : uint8_t { + INCREMENTAL = 1, +}; + +template +struct format_traits { + using Encoder = typename T::Encoder; + using Decoder = typename T::Decoder; + using HashType = typename T::HashType; + using HashList = typename T::HashList; +}; + +class Packet; + +template +class ManifestEncoder { + public: + virtual ~ManifestEncoder() = default; + + ManifestEncoder encode() { + return static_cast(*this).encodeImpl(); + } + + ManifestEncoder& clear() { + return static_cast(*this).clearImpl(); + } + + ManifestEncoder& setManifestType(ManifestType type) { + return static_cast(*this).setManifestTypeImpl(type); + } + + ManifestEncoder& setHashAlgorithm(HashAlgorithm hash) { + return static_cast(*this).setHashAlgorithmImpl(hash); + } + + ManifestEncoder& setFinalChunkNumber(uint32_t final_chunk) { + return static_cast(*this).setFinalChunkImpl(final_chunk); + } + + ManifestEncoder& setNextSegmentCalculationStrategy( + NextSegmentCalculationStrategy strategy) { + return static_cast(*this) + .setNextSegmentCalculationStrategyImpl(strategy); + } + + template < + typename T, + typename = std::enable_if_t>, core::Name>::value>> + ManifestEncoder& setBaseName(T&& name) { + return static_cast(*this).setBaseNameImpl(name); + } + + template + ManifestEncoder& addSuffixAndHash(uint32_t suffix, Hash&& hash) { + return static_cast(*this).addSuffixAndHashImpl( + suffix, std::forward(hash)); + } + + ManifestEncoder& setIsFinalManifest(bool is_last) { + return static_cast(*this).setIsFinalManifestImpl(is_last); + } + + ManifestEncoder& setVersion(ManifestVersion version) { + return static_cast(*this).setVersionImpl(version); + } + + std::size_t estimateSerializedLength(std::size_t number_of_entries) { + return static_cast(*this).estimateSerializedLengthImpl( + number_of_entries); + } + + ManifestEncoder& update() { + return static_cast(*this).updateImpl(); + } + + ManifestEncoder& setFinalBlockNumber(std::uint32_t final_block_number) { + return static_cast(*this).setFinalBlockNumberImpl( + final_block_number); + } + + static std::size_t getManifestHeaderSize() { + return Implementation::getManifestHeaderSizeImpl(); + } +}; + +template +class ManifestDecoder { + public: + virtual ~ManifestDecoder() = default; + + ManifestDecoder& clear() { + return static_cast(*this).clearImpl(); + } + + void decode() { static_cast(*this).decodeImpl(); } + + ManifestType getManifestType() const { + return static_cast(*this).getManifestTypeImpl(); + } + + HashAlgorithm getHashAlgorithm() const { + return static_cast(*this).getHashAlgorithmImpl(); + } + + uint32_t getFinalChunkNumber() const { + return static_cast(*this).getFinalChunkImpl(); + } + + NextSegmentCalculationStrategy getNextSegmentCalculationStrategy() const { + return static_cast(*this) + .getNextSegmentCalculationStrategyImpl(); + } + + core::Name getBaseName() const { + return static_cast(*this).getBaseNameImpl(); + } + + auto getSuffixHashList() { + return static_cast(*this).getSuffixHashListImpl(); + } + + bool getIsFinalManifest() const { + return static_cast(*this).getIsFinalManifestImpl(); + } + + ManifestVersion getVersion() const { + return static_cast(*this).getVersionImpl(); + } + + std::size_t estimateSerializedLength(std::size_t number_of_entries) const { + return static_cast(*this) + .estimateSerializedLengthImpl(number_of_entries); + } + + uint32_t getFinalBlockNumber() const { + return static_cast(*this).getFinalBlockNumberImpl(); + } +}; + +} // namespace core + +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/manifest_format_fixed.cc b/libtransport/src/hicn/transport/core/manifest_format_fixed.cc new file mode 100755 index 000000000..f26f20adb --- /dev/null +++ b/libtransport/src/hicn/transport/core/manifest_format_fixed.cc @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +namespace transport { + +namespace core { + +// TODO use preallocated pool of membufs +FixedManifestEncoder::FixedManifestEncoder(Packet& packet) + : packet_(packet), + max_size_(Packet::default_mtu - packet_.headerSize()), + manifest_( + utils::MemBuf::create(Packet::default_mtu - packet_.headerSize())), + manifest_header_( + reinterpret_cast(manifest_->writableData())), + manifest_entries_(reinterpret_cast( + manifest_->writableData() + sizeof(ManifestHeader))), + current_entry_(0) {} + +FixedManifestEncoder::~FixedManifestEncoder() {} + +FixedManifestEncoder& FixedManifestEncoder::encodeImpl() { + packet_.appendPayload(std::move(manifest_)); + return *this; +} + +FixedManifestEncoder& FixedManifestEncoder::clearImpl() { + manifest_ = utils::MemBuf::create(Packet::default_mtu - packet_.headerSize()); + return *this; +} + +FixedManifestEncoder& FixedManifestEncoder::setHashAlgorithmImpl( + HashAlgorithm algorithm) { + manifest_header_->hash_algorithm = static_cast(algorithm); + return *this; +} + +FixedManifestEncoder& FixedManifestEncoder::setManifestTypeImpl( + ManifestType manifest_type) { + manifest_header_->manifest_type = static_cast(manifest_type); + return *this; +} + +FixedManifestEncoder& +FixedManifestEncoder::setNextSegmentCalculationStrategyImpl( + NextSegmentCalculationStrategy strategy) { + manifest_header_->next_segment_strategy = static_cast(strategy); + return *this; +} + +FixedManifestEncoder& FixedManifestEncoder::setBaseNameImpl( + const core::Name& base_name) { + base_name.copyToDestination( + reinterpret_cast(&manifest_header_->prefix[0]), false); + manifest_header_->flags.ipv6 = + base_name.getAddressFamily() == AF_INET6 ? 1_U8 : 0_U8; + return *this; +} + +FixedManifestEncoder& FixedManifestEncoder::addSuffixAndHashImpl( + uint32_t suffix, const utils::CryptoHash& hash) { + auto _hash = hash.getDigest(); + addSuffixHashBytes(suffix, _hash.data(), _hash.length()); + return *this; +} + +void FixedManifestEncoder::addSuffixHashBytes(uint32_t suffix, + const uint8_t* hash, + std::size_t length) { + manifest_entries_[current_entry_].suffix = utils::hton(suffix); + // std::copy(hash, hash + length, + // manifest_entries_[current_entry_].hash); + std::memcpy( + reinterpret_cast(manifest_entries_[current_entry_].hash), hash, + length); + + manifest_header_->number_of_entries++; + current_entry_++; + + if (TRANSPORT_EXPECT_FALSE(estimateSerializedLengthImpl() > max_size_)) { + throw errors::RuntimeException("Manifest size exceeded the packet MTU!"); + } +} + +FixedManifestEncoder& FixedManifestEncoder::setIsFinalManifestImpl( + bool is_last) { + manifest_header_->flags.is_last = static_cast(is_last); + return *this; +} + +FixedManifestEncoder& FixedManifestEncoder::setVersionImpl( + ManifestVersion version) { + manifest_header_->version = static_cast(version); + return *this; +} + +std::size_t FixedManifestEncoder::estimateSerializedLengthImpl( + std::size_t additional_entries) { + return sizeof(ManifestHeader) + + (manifest_header_->number_of_entries + additional_entries) * + sizeof(ManifestEntry); +} + +FixedManifestEncoder& FixedManifestEncoder::updateImpl() { + max_size_ = Packet::default_mtu - packet_.headerSize(); + manifest_header_ = reinterpret_cast( + const_cast(packet_.getPayload().data())); + manifest_entries_ = reinterpret_cast( + const_cast(packet_.getPayload().data()) + + sizeof(ManifestHeader)); + return *this; +} + +FixedManifestEncoder& FixedManifestEncoder::setFinalBlockNumberImpl( + std::uint32_t final_block_number) { + manifest_header_->final_block_number = utils::hton(final_block_number); + return *this; +} + +std::size_t FixedManifestEncoder::getManifestHeaderSizeImpl() { + return sizeof(ManifestHeader); +} + +FixedManifestDecoder::FixedManifestDecoder(Packet& packet) + : packet_(packet), + manifest_header_(reinterpret_cast( + const_cast(packet_.getPayload().data()))), + manifest_entries_(reinterpret_cast( + const_cast(packet_.getPayload().data()) + + sizeof(ManifestHeader))) {} + +FixedManifestDecoder::~FixedManifestDecoder() {} + +void FixedManifestDecoder::decodeImpl() { + std::size_t packet_size = packet_.payloadSize(); + + if (packet_size < sizeof(ManifestHeader) || + packet_size < estimateSerializedLengthImpl()) { + throw errors::RuntimeException( + "The packet does not match expected manifest size."); + } +} + +FixedManifestDecoder& FixedManifestDecoder::clearImpl() { return *this; } + +ManifestType FixedManifestDecoder::getManifestTypeImpl() const { + return static_cast(manifest_header_->manifest_type); +} + +HashAlgorithm FixedManifestDecoder::getHashAlgorithmImpl() const { + return static_cast(manifest_header_->hash_algorithm); +} + +NextSegmentCalculationStrategy +FixedManifestDecoder::getNextSegmentCalculationStrategyImpl() const { + return static_cast( + manifest_header_->next_segment_strategy); +} + +typename Fixed::SuffixList FixedManifestDecoder::getSuffixHashListImpl() { + typename Fixed::SuffixList hash_list; + + for (int i = 0; i < manifest_header_->number_of_entries; i++) { + hash_list.insert(hash_list.end(), + std::make_pair(utils::ntoh(manifest_entries_[i].suffix), + reinterpret_cast( + &manifest_entries_[i].hash[0]))); + } + + return hash_list; +} + +core::Name FixedManifestDecoder::getBaseNameImpl() const { + if (static_cast(manifest_header_->flags.ipv6)) { + return core::Name(AF_INET6, + reinterpret_cast(&manifest_header_->prefix)); + } else { + return core::Name(AF_INET, + reinterpret_cast(&manifest_header_->prefix)); + } +} + +bool FixedManifestDecoder::getIsFinalManifestImpl() const { + return static_cast(manifest_header_->flags.is_last); +} + +ManifestVersion FixedManifestDecoder::getVersionImpl() const { + return static_cast(manifest_header_->version); +} + +std::size_t FixedManifestDecoder::estimateSerializedLengthImpl( + std::size_t additional_entries) const { + return sizeof(ManifestHeader) + + (additional_entries + manifest_header_->number_of_entries) * + sizeof(ManifestEntry); +} + +uint32_t FixedManifestDecoder::getFinalBlockNumberImpl() const { + return utils::ntoh(manifest_header_->final_block_number); +} + +} // end namespace core + +} // end namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/manifest_format_fixed.h b/libtransport/src/hicn/transport/core/manifest_format_fixed.h new file mode 100755 index 000000000..66825e2f4 --- /dev/null +++ b/libtransport/src/hicn/transport/core/manifest_format_fixed.h @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include + +namespace transport { + +namespace core { + +// 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| MType |HashAlg|NextStr| Flags |NumberOfEntries| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Final Block Number | +// +---------------------------------------------------------------| +// | | +// + + +// | | +// + Prefix + +// | | +// + + +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Suffix | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Hash Value | +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +class FixedManifestEncoder; +class FixedManifestDecoder; +class Packet; + +struct Fixed { + using Encoder = FixedManifestEncoder; + using Decoder = FixedManifestDecoder; + using HashType = utils::CryptoHash; + using SuffixList = std::list>; +}; + +struct Flags { + std::uint8_t ipv6 : 1; + std::uint8_t is_last : 1; + std::uint8_t unused : 6; +}; + +struct ManifestEntry { + std::uint32_t suffix; + std::uint32_t hash[8]; +}; + +struct ManifestHeader { + std::uint8_t version : 4; + std::uint8_t manifest_type : 4; + std::uint8_t hash_algorithm : 4; + std::uint8_t next_segment_strategy : 4; + Flags flags; + std::uint8_t number_of_entries; + std::uint32_t final_block_number; + std::uint32_t prefix[4]; + ManifestEntry entries[0]; +}; + +static const constexpr std::uint8_t manifest_version = 1; + +class FixedManifestEncoder : public ManifestEncoder { + public: + FixedManifestEncoder(Packet& packet); + + ~FixedManifestEncoder(); + + FixedManifestEncoder& encodeImpl(); + + FixedManifestEncoder& clearImpl(); + + FixedManifestEncoder& setManifestTypeImpl(ManifestType manifest_type); + + FixedManifestEncoder& setHashAlgorithmImpl(HashAlgorithm algorithm); + + FixedManifestEncoder& setNextSegmentCalculationStrategyImpl( + NextSegmentCalculationStrategy strategy); + + FixedManifestEncoder& setBaseNameImpl(const core::Name& base_name); + + FixedManifestEncoder& addSuffixAndHashImpl(uint32_t suffix, + const utils::CryptoHash& hash); + + FixedManifestEncoder& setIsFinalManifestImpl(bool is_last); + + FixedManifestEncoder& setVersionImpl(ManifestVersion version); + + std::size_t estimateSerializedLengthImpl(std::size_t additional_entries = 0); + + FixedManifestEncoder& updateImpl(); + + FixedManifestEncoder& setFinalBlockNumberImpl( + std::uint32_t final_block_number); + + static std::size_t getManifestHeaderSizeImpl(); + + private: + void addSuffixHashBytes(uint32_t suffix, const uint8_t* hash, + std::size_t length); + + Packet& packet_; + std::size_t max_size_; + std::unique_ptr manifest_; + ManifestHeader* manifest_header_; + ManifestEntry* manifest_entries_; + std::size_t current_entry_; +}; + +class FixedManifestDecoder : public ManifestDecoder { + public: + FixedManifestDecoder(Packet& packet); + + ~FixedManifestDecoder(); + + void decodeImpl(); + + FixedManifestDecoder& clearImpl(); + + ManifestType getManifestTypeImpl() const; + + HashAlgorithm getHashAlgorithmImpl() const; + + NextSegmentCalculationStrategy getNextSegmentCalculationStrategyImpl() const; + + typename Fixed::SuffixList getSuffixHashListImpl(); + + core::Name getBaseNameImpl() const; + + bool getIsFinalManifestImpl() const; + + std::size_t estimateSerializedLengthImpl( + std::size_t additional_entries = 0) const; + + ManifestVersion getVersionImpl() const; + + uint32_t getFinalBlockNumberImpl() const; + + private: + Packet& packet_; + ManifestHeader* manifest_header_; + ManifestEntry* manifest_entries_; +}; + +} // namespace core + +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/manifest_format_json_jsoncpp.cc b/libtransport/src/hicn/transport/core/manifest_format_json_jsoncpp.cc new file mode 100755 index 000000000..512cdba5b --- /dev/null +++ b/libtransport/src/hicn/transport/core/manifest_format_json_jsoncpp.cc @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +namespace transport { + +namespace core { + +namespace { + +template +TRANSPORT_ALWAYS_INLINE void checkPointer(T* pointer) { + if (pointer == nullptr) { + throw errors::NullPointerException(); + } +} + +template +TRANSPORT_ALWAYS_INLINE void setValueToJson(Json::Value& root, EnumType value) { + root[JSONKey::key] = static_cast(value); +} + +template +TRANSPORT_ALWAYS_INLINE EnumType getValueFromJson(const Json::Value& root) { + return static_cast(root[JSONKey::key].asUInt()); +}; + +} // namespace + +JSONManifestEncoder::JSONManifestEncoder(Packet& packet) : packet_(packet) {} + +JSONManifestEncoder::~JSONManifestEncoder() {} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& JSONManifestEncoder::encodeImpl() { + Json::StreamWriterBuilder writer_builder; + Json::StreamWriter* fast_writer = writer_builder.newStreamWriter(); + + asio::streambuf strbuf; + strbuf.prepare(1500); + std::ostream stream(&strbuf); + fast_writer->write(root_, &stream); + + const uint8_t* buffer = asio::buffer_cast(strbuf.data()); + + packet_.setPayload(buffer, strbuf.size()); + + delete fast_writer; + + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& JSONManifestEncoder::clearImpl() { + root_.clear(); + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setHashAlgorithmImpl(HashAlgorithm algorithm) { + setValueToJson(root_, algorithm); + return *this; +} + +JSONManifestEncoder& JSONManifestEncoder::setManifestTypeImpl( + ManifestType manifest_type) { + setValueToJson(root_, manifest_type); + return *this; +} + +JSONManifestEncoder& JSONManifestEncoder::setNextSegmentCalculationStrategyImpl( + NextSegmentCalculationStrategy strategy) { + setValueToJson(root_, strategy); + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setBaseNameImpl(const core::Name& base_name) { + root_[JSONKey::key] = base_name.toString().c_str(); + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::addSuffixAndHashImpl(uint32_t suffix, + const utils::CryptoHash& hash) { + throw errors::NotImplementedException(); + // Json::Value value(Json::arrayValue); + // value.append(Json::Value(suffix)); + // value.append(Json::Value(Json::Value::UInt64 (hash))); + // root_[JSONKey::key].append(value); + + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setIsFinalManifestImpl(bool is_last) { + root_[JSONKey::final_manifest] = is_last; + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setVersionImpl(ManifestVersion version) { + setValueToJson(root_, version); + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setSuffixHashListImpl( + const typename JSON::SuffixList& name_hash_list) { + throw errors::NotImplementedException(); + // for (auto &suffix : name_hash_list) { + // addSuffixAndHashImpl(suffix.first, suffix.second); + // } + // + // return *this; +} + +TRANSPORT_ALWAYS_INLINE std::size_t +JSONManifestEncoder::estimateSerializedLengthImpl( + std::size_t number_of_entries) { + Json::StreamWriterBuilder writer_builder; + Json::StreamWriter* fast_writer = writer_builder.newStreamWriter(); + + asio::streambuf strbuf; + strbuf.prepare(1500); + std::ostream stream(&strbuf); + fast_writer->write(root_, &stream); + + return strbuf.size(); +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& JSONManifestEncoder::updateImpl() { + throw errors::NotImplementedException(); +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setFinalBlockNumberImpl(std::uint32_t final_block_number) { + throw errors::NotImplementedException(); +} + +TRANSPORT_ALWAYS_INLINE std::size_t +JSONManifestEncoder::getManifestHeaderSizeImpl() { + return 0; +} + +JSONManifestDecoder::JSONManifestDecoder(Packet& packet) : packet_(packet) {} + +JSONManifestDecoder::~JSONManifestDecoder() {} + +TRANSPORT_ALWAYS_INLINE void JSONManifestDecoder::decodeImpl() { + auto array = packet_.getPayload(); + auto payload = array.data(); + auto payload_size = array.length(); + + Json::CharReaderBuilder reader_builder; + Json::CharReader* reader = reader_builder.newCharReader(); + std::string errors; + + if (!reader->parse((char*)payload, (char*)payload + payload_size, &root_, + &errors)) { + TRANSPORT_LOGE("Error parsing manifest!"); + TRANSPORT_LOGE("%s", errors.c_str()); + + delete reader; + + throw errors::MalformedPacketException(); + } + + delete reader; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestDecoder& JSONManifestDecoder::clearImpl() { + root_.clear(); + return *this; +} + +TRANSPORT_ALWAYS_INLINE ManifestType +JSONManifestDecoder::getManifestTypeImpl() const { + return getValueFromJson(root_); +} + +TRANSPORT_ALWAYS_INLINE HashAlgorithm +JSONManifestDecoder::getHashAlgorithmImpl() const { + return getValueFromJson(root_); +} + +TRANSPORT_ALWAYS_INLINE NextSegmentCalculationStrategy +JSONManifestDecoder::getNextSegmentCalculationStrategyImpl() const { + return getValueFromJson(root_); +} + +TRANSPORT_ALWAYS_INLINE typename JSON::SuffixList +JSONManifestDecoder::getSuffixHashListImpl() { + throw errors::NotImplementedException(); + // SuffixHashList hash_list; + // + // Json::Value &array = root_[JSONKey::key]; + // + // for (Json::Value::ArrayIndex i = 0; + // i != array.size(); + // i++) { + // hash_list[array[i][0].asUInt()] = array[i][1].asUInt64(); + // } + // + // return hash_list; +} + +TRANSPORT_ALWAYS_INLINE core::Name JSONManifestDecoder::getBaseNameImpl() + const { + return core::Name(root_[JSONKey::key].asCString()); +} + +TRANSPORT_ALWAYS_INLINE bool JSONManifestDecoder::getIsFinalManifestImpl() + const { + return root_[JSONKey::final_manifest].asBool(); +} + +TRANSPORT_ALWAYS_INLINE ManifestVersion +JSONManifestDecoder::getVersionImpl() const { + return getValueFromJson(root_); +} + +TRANSPORT_ALWAYS_INLINE uint32_t +JSONManifestDecoder::getFinalBlockNumberImpl() const { + return 0; +} + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/manifest_format_json_jsoncpp.h b/libtransport/src/hicn/transport/core/manifest_format_json_jsoncpp.h new file mode 100755 index 000000000..39f0cf351 --- /dev/null +++ b/libtransport/src/hicn/transport/core/manifest_format_json_jsoncpp.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#if defined(__APPLE__) || defined(__ANDROID__) +#include +#else +#include +#endif /* __APPLE__ || __ANDROID__*/ + +#include + +namespace transport { + +namespace core { + +class JSONManifestEncoder; +class JSONManifestDecoder; +class Packet; + +struct JSON { + using Encoder = JSONManifestEncoder; + using Decoder = JSONManifestDecoder; + using HashType = utils::CryptoHash; + using SuffixList = std::unordered_map; +}; + +template +struct JSONKey; + +template <> +struct JSONKey { + static const constexpr char* key = "manifest_version"; +}; + +template <> +struct JSONKey { + static const constexpr char* key = "hash_algorithm"; +}; + +template <> +struct JSONKey { + static const constexpr char* key = "manifest_type"; +}; + +template <> +struct JSONKey { + static const constexpr char* key = "next_segment_strategy"; +}; + +template <> +struct JSONKey { + static const constexpr char* key = "suffix_hash_list"; +}; + +template <> +struct JSONKey { + static const constexpr char* key = "base_name"; +}; + +template <> +struct JSONKey { + static const constexpr char* final_manifest = "final_manifest"; +}; + +class JSONManifestEncoder : public ManifestEncoder { + public: + JSONManifestEncoder(Packet& packet); + + ~JSONManifestEncoder() override; + + JSONManifestEncoder& encodeImpl(); + + JSONManifestEncoder& clearImpl(); + + JSONManifestEncoder& setManifestTypeImpl(ManifestType manifest_type); + + JSONManifestEncoder& setHashAlgorithmImpl(HashAlgorithm algorithm); + + JSONManifestEncoder& setNextSegmentCalculationStrategyImpl( + NextSegmentCalculationStrategy strategy); + + JSONManifestEncoder& setSuffixHashListImpl( + const typename JSON::SuffixList& name_hash_list); + + JSONManifestEncoder& setBaseNameImpl(const core::Name& base_name); + + JSONManifestEncoder& addSuffixAndHashImpl(uint32_t suffix, + const utils::CryptoHash& hash); + + JSONManifestEncoder& setIsFinalManifestImpl(bool is_last); + + JSONManifestEncoder& setVersionImpl(ManifestVersion version); + + std::size_t estimateSerializedLengthImpl(std::size_t number_of_entries); + + JSONManifestEncoder& updateImpl(); + + JSONManifestEncoder& setFinalBlockNumberImpl( + std::uint32_t final_block_number); + + static std::size_t getManifestHeaderSizeImpl(); + + private: + Packet& packet_; + Json::Value root_; +}; + +class JSONManifestDecoder : public ManifestDecoder { + public: + JSONManifestDecoder(Packet& packet); + + ~JSONManifestDecoder() override; + + void decodeImpl(); + + JSONManifestDecoder& clearImpl(); + + ManifestType getManifestTypeImpl() const; + + HashAlgorithm getHashAlgorithmImpl() const; + + uint32_t getFinalChunkImpl() const; + + NextSegmentCalculationStrategy getNextSegmentCalculationStrategyImpl() const; + + typename JSON::SuffixList getSuffixHashListImpl(); + + core::Name getBaseNameImpl() const; + + bool getIsFinalManifestImpl() const; + + std::size_t estimateSerializedLengthImpl(std::size_t number_of_entries) const; + + ManifestVersion getVersionImpl() const; + + uint32_t getFinalBlockNumberImpl() const; + + private: + Packet& packet_; + Json::Value root_; +}; + +} // namespace core + +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/manifest_format_json_libparc_deprecated.cc b/libtransport/src/hicn/transport/core/manifest_format_json_libparc_deprecated.cc new file mode 100755 index 000000000..d0365d2fe --- /dev/null +++ b/libtransport/src/hicn/transport/core/manifest_format_json_libparc_deprecated.cc @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +extern "C" { +#include +} + +namespace transport { + +namespace core { + +namespace { + +template +TRANSPORT_ALWAYS_INLINE void checkPointer(T* pointer) { + if (pointer == nullptr) { + throw errors::NullPointerException(); + } +} + +template +TRANSPORT_ALWAYS_INLINE void setValueToJson(PARCJSON* root, EnumType value) { + parcJSON_AddInteger(root, JSONKey::key, + static_cast(value)); +} + +template +TRANSPORT_ALWAYS_INLINE EnumType getValueFromJson(PARCJSON* root) { + checkPointer(root); + + PARCJSONValue* value = parcJSON_GetValueByName(root, JSONKey::key); + + EnumType ret = static_cast(parcJSONValue_GetInteger(value)); + // parcJSONValue_Release(&value); + + return ret; +}; + +} // namespace + +JSONManifestEncoder::JSONManifestEncoder() : root_(parcJSON_Create()) { + parcJSON_Acquire(root_); +} + +JSONManifestEncoder::~JSONManifestEncoder() { + if (root_) { + parcJSON_Release(&root_); + } +} + +TRANSPORT_ALWAYS_INLINE SONManifestEncoder& JSONManifestEncoder::encodeImpl( + Packet& packet) { + char* json_string = parcJSON_ToString(root_); + packet.setPayload(reinterpret_cast(json_string), + std::strlen(json_string)); + parcMemory_Deallocate(&json_string); + + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& JSONManifestEncoder::clearImpl() { + if (root_) { + parcJSON_Release(&root_); + } + + root_ = parcJSON_Create(); + + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setHashAlgorithmImpl(HashAlgorithm algorithm) { + setValueToJson(root_, algorithm); + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setManifestTypeImpl(ManifestType manifest_type) { + setValueToJson(root_, manifest_type); + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setNextSegmentCalculationStrategyImpl( + NextSegmentCalculationStrategy strategy) { + setValueToJson(root_, strategy); + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setBaseNameImpl(const core::Name& base_name) { + parcJSON_AddString(root_, JSONKey::key, + base_name.toString().c_str()); + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::addSuffixAndHashImpl(uint32_t suffix, + utils::CryptoHash& hash) { + throw errors::NotImplementedException(); + // PARCJSONValue *value = parcJSON_GetValueByName(root_, + // JSONKey::key); + // + // // Create the pair to store in the array. + // // It will be segment number + Hash of the segment + // PARCJSONArray * pair = parcJSONArray_Create(); + // + // PARCJSONValue *v = parcJSONValue_CreateFromInteger(suffix); + // parcJSONArray_AddValue(pair, v); + // parcJSONValue_Release(&v); + // + // v = parcJSONValue_CreateFromInteger(hash); + // parcJSONArray_AddValue(pair, v); + // parcJSONValue_Release(&v); + // + // if (value == nullptr /* || !parcJSONValue_IsArray(value) */) { + // // Create the array + // PARCJSONArray *array = parcJSONArray_Create(); + // parcJSON_AddArray(root_, + // JSONKey::key, + // array); + // parcJSONArray_Release(&array); + // + // value = parcJSON_GetValueByName(root_, JSONKey::key); + // } + // + // v = parcJSONValue_CreateFromJSONArray(pair); + // parcJSONArray_AddValue(parcJSONValue_GetArray(value), v); + // parcJSONValue_Release(&v); + // + // parcJSONArray_Release(&pair); + // // parcJSONValue_Release(&value); + + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setIsFinalManifestImpl(bool is_last) { + parcJSON_AddBoolean(root_, JSONKey::final_manifest, is_last); + + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setSuffixHashListImpl( + const SuffixHashList& name_hash_list) { + for (auto& suffix : name_hash_list) { + addSuffixAndHashImpl(suffix.first, suffix.second); + } + + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestDecoder::JSONManifestDecoder() + : root_(nullptr) {} + +TRANSPORT_ALWAYS_INLINE JSONManifestDecoder::~JSONManifestDecoder() { + if (root_) { + parcJSON_Release(&root_); + } +} + +TRANSPORT_ALWAYS_INLINE void JSONManifestDecoder::decodeImpl( + const uint8_t* payload, std::size_t payload_size) { + PARCBuffer* b = parcBuffer_Wrap(const_cast(payload), payload_size, + 0, payload_size); + clearImpl(); + + root_ = parcJSON_ParseBuffer(b); + parcBuffer_Release(&b); + + char* str = parcJSON_ToString(root_); +} + +TRANSPORT_ALWAYS_INLINE JSONManifestDecoder& JSONManifestDecoder::clearImpl() { + if (root_) { + parcJSON_Release(&root_); + } + + return *this; +} + +TRANSPORT_ALWAYS_INLINE ManifestType +JSONManifestDecoder::getManifestTypeImpl() const { + return getValueFromJson(root_); +} + +TRANSPORT_ALWAYS_INLINE HashAlgorithm +JSONManifestDecoder::getHashAlgorithmImpl() const { + return getValueFromJson(root_); +} + +TRANSPORT_ALWAYS_INLINE NextSegmentCalculationStrategy +JSONManifestDecoder::getNextSegmentCalculationStrategyImpl() const { + return getValueFromJson(root_); +} + +TRANSPORT_ALWAYS_INLINE SuffixHashList +JSONManifestDecoder::getSuffixHashListImpl() { + throw errors::NotImplementedException(); + // SuffixHashList hash_list; + // + // char * str = parcJSON_ToString(root_); + // + // PARCJSONValue *value = parcJSON_GetValueByName(root_, + // JSONKey::key); + // + // if (value == nullptr || !parcJSONValue_IsArray(value)) { + // throw errors::RuntimeException("Manifest does not contain suffix-hash + // list"); + // } + // + // PARCJSONArray *array = parcJSONValue_GetArray(value); + // std::size_t array_size = parcJSONArray_GetLength(array); + // + // for (std::size_t i = 0; i < array_size; i++) { + // PARCJSONValue *v = parcJSONArray_GetValue(array, i); + // checkPointer(v); + // PARCJSONArray *a = parcJSONValue_GetArray(v); + // PARCJSONValue *_suffix = parcJSONArray_GetValue(a, 0); + // PARCJSONValue *_hash = parcJSONArray_GetValue(a, 1); + // + // uint32_t value1 = + // static_cast(parcJSONValue_GetInteger(_suffix)); uint64_t + // value2 = static_cast(parcJSONValue_GetInteger(_hash)); + // + // hash_list[static_cast(parcJSONValue_GetInteger(_suffix))] = + // static_cast(parcJSONValue_GetInteger(_hash)); + // + //// parcJSONValue_Release(&_hash); + //// parcJSONValue_Release(&_suffix); + //// parcJSONArray_Release(&a); + //// parcJSONValue_Release(&v); + // } + // + //// parcJSONArray_Release(&array); + //// parcJSONValue_Release(&value); + // + // char * str2 = parcJSON_ToString(root_); + // + // return hash_list; +} + +TRANSPORT_ALWAYS_INLINE core::Name JSONManifestDecoder::getBaseNameImpl() + const { + checkPointer(root_); + PARCJSONValue* value = + parcJSON_GetValueByName(root_, JSONKey::key); + + PARCBuffer* b = parcJSONValue_GetString(value); + char* string = parcBuffer_ToString(b); + + core::Name ret(string); + + // parcJSONValue_Release(&value); + parcMemory_Deallocate(&string); + + return ret; +} + +TRANSPORT_ALWAYS_INLINE bool JSONManifestDecoder::getIsFinalManifestImpl() { + checkPointer(root_); + PARCJSONValue* value = + parcJSON_GetValueByName(root_, JSONKey::final_manifest); + + bool ret = parcJSONValue_GetBoolean(value); + + // parcJSONValue_Release(&value); + + return ret; +} + +TRANSPORT_ALWAYS_INLINE std::size_t +JSONManifestDecoder::estimateSerializedLengthImpl( + std::size_t number_of_entries) { + return 0; +} + +} // end namespace core + +} // end namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/manifest_format_json_libparc_deprecated.h b/libtransport/src/hicn/transport/core/manifest_format_json_libparc_deprecated.h new file mode 100755 index 000000000..28c6c1b40 --- /dev/null +++ b/libtransport/src/hicn/transport/core/manifest_format_json_libparc_deprecated.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +extern "C" { +#include +} + +#include + +namespace transport { + +namespace core { + +class JSONManifestEncoder; +class JSONManifestDecoder; +class Packet; + +struct JSON { + using Encoder = JSONManifestEncoder; + using Decoder = JSONManifestDecoder; +}; + +template +struct JSONKey; + +template <> +struct JSONKey { + static const constexpr char* key = "hash_algorithm"; +}; + +template <> +struct JSONKey { + static const constexpr char* key = "manifest_type"; +}; + +template <> +struct JSONKey { + static const constexpr char* key = "next_segment_strategy"; +}; + +template <> +struct JSONKey { + static const constexpr char* key = "name_hash_list"; +}; + +template <> +struct JSONKey { + static const constexpr char* key = "suffix_hash_list"; +}; + +template <> +struct JSONKey { + static const constexpr char* key = "base_name"; +}; + +template <> +struct JSONKey { + static const constexpr char* final_manifest = "final_manifest"; +}; + +// template <> +// struct JSONKey { +// static const std::string key = "name_hash_list"; +//}; + +// namespace JSONManifestEncoding { +// static const std::string base_name = "base_name"; +// static const std::string final_chunk_number = "final_chunk_number"; +// static const std::string hash_algorithm = "hash_algorithm"; +// static const std::string manifest_type = "manifest_type"; +// static const std::string name_hash_list = "name_hash_list"; +// static const std::string next_segment_strategy = "next_segment_strategy"; +//} + +class JSONManifestEncoder : public ManifestEncoder { + public: + JSONManifestEncoder(); + + ~JSONManifestEncoder(); + + JSONManifestEncoder& encodeImpl(Packet& packet); + + JSONManifestEncoder& clearImpl(); + + JSONManifestEncoder& setManifestTypeImpl(ManifestType manifest_type); + + JSONManifestEncoder& setHashAlgorithmImpl(HashAlgorithm algorithm); + + JSONManifestEncoder& setNextSegmentCalculationStrategyImpl( + NextSegmentCalculationStrategy strategy); + + JSONManifestEncoder& setSuffixHashListImpl( + const SuffixHashList& name_hash_list); + + JSONManifestEncoder& setBaseNameImpl(const core::Name& base_name); + + JSONManifestEncoder& addSuffixAndHashImpl(uint32_t suffix, uint64_t hash); + + JSONManifestEncoder& setIsFinalManifestImpl(bool is_last); + + private: + PARCJSON* root_; +}; + +class JSONManifestDecoder : public ManifestDecoder { + public: + JSONManifestDecoder(); + + ~JSONManifestDecoder(); + + void decodeImpl(const uint8_t* payload, std::size_t payload_size); + + JSONManifestDecoder& clearImpl(); + + ManifestType getManifestTypeImpl() const; + + HashAlgorithm getHashAlgorithmImpl() const; + + uint32_t getFinalChunkImpl() const; + + NextSegmentCalculationStrategy getNextSegmentCalculationStrategyImpl() const; + + SuffixHashList getSuffixHashListImpl(); + + core::Name getBaseNameImpl() const; + + bool getIsFinalManifestImpl(); + + private: + PARCJSON* root_; +}; + +} // namespace core + +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/manifest_inline.h b/libtransport/src/hicn/transport/core/manifest_inline.h new file mode 100755 index 000000000..fd1a9abd3 --- /dev/null +++ b/libtransport/src/hicn/transport/core/manifest_inline.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +#include + +namespace transport { + +namespace core { + +template +class ManifestInline + : public Manifest> { + using ManifestBase = + Manifest>; + using HashType = typename FormatTraits::HashType; + using SuffixList = typename FormatTraits::SuffixList; + + public: + ManifestInline() : ManifestBase() {} + + ManifestInline(const core::Name& name) : ManifestBase(name) {} + + template + ManifestInline(T&& base) : ManifestBase(std::forward(base)) {} + + static TRANSPORT_ALWAYS_INLINE ManifestInline* createManifest( + const core::Name& manifest_name, ManifestVersion version, + ManifestType type, HashAlgorithm algorithm, bool is_last, + const Name& base_name, NextSegmentCalculationStrategy strategy, + std::size_t signature_size) { + auto manifest = new ManifestInline(manifest_name); + manifest->setSignatureSize(signature_size); + manifest->setVersion(version); + manifest->setManifestType(type); + manifest->setHashAlgorithm(algorithm); + manifest->setFinalManifest(is_last); + manifest->setBaseName(base_name); + manifest->setNextSegmentCalculationStrategy(strategy); + + return manifest; + } + + ManifestInline& encodeImpl() { + ManifestBase::encoder_.encode(); + return *this; + } + + ManifestInline& decodeImpl() { + base_name_ = ManifestBase::decoder_.getBaseName(); + next_segment_strategy_ = + ManifestBase::decoder_.getNextSegmentCalculationStrategy(); + suffix_hash_map_ = ManifestBase::decoder_.getSuffixHashList(); + + return *this; + } + + std::size_t estimateManifestSizeImpl(std::size_t additional_entries = 0) { + return ManifestBase::encoder_.estimateSerializedLength(additional_entries); + } + + ManifestInline& setBaseName(const Name& name) { + base_name_ = name; + ManifestBase::encoder_.setBaseName(base_name_); + return *this; + } + + const Name& getBaseName() { return base_name_; } + + ManifestInline& addSuffixHash(uint32_t suffix, const HashType& hash) { + ManifestBase::encoder_.addSuffixAndHash(suffix, hash); + return *this; + } + + // Call this function only after the decode function! + const SuffixList& getSuffixList() { return suffix_hash_map_; } + + ManifestInline& setNextSegmentCalculationStrategy( + NextSegmentCalculationStrategy strategy) { + next_segment_strategy_ = strategy; + ManifestBase::encoder_.setNextSegmentCalculationStrategy( + next_segment_strategy_); + return *this; + } + + NextSegmentCalculationStrategy getNextSegmentCalculationStrategy() { + return next_segment_strategy_; + } + + private: + core::Name base_name_; + NextSegmentCalculationStrategy next_segment_strategy_; + SuffixList suffix_hash_map_; +}; + +} // end namespace core + +} // end namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/memif_binary_api.c b/libtransport/src/hicn/transport/core/memif_binary_api.c new file mode 100755 index 000000000..b443b51ce --- /dev/null +++ b/libtransport/src/hicn/transport/core/memif_binary_api.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#ifdef __vpp__ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +// uword unformat_sw_if_index (unformat_input_t * input, va_list * args); + +/* Declare message IDs */ +#include + +#define vl_msg_name_crc_list +#include +#undef vl_msg_name_crc_list + +#define vl_typedefs +#define vl_endianfun +#define vl_print(handle, ...) +#define vl_printfun +#define vl_api_version(n, v) static u32 api_version = (v); +#define vl_msg_name_crc_list +#include +#undef vl_msg_name_crc_list +#undef vl_api_version +#undef vl_printfun +#undef vl_endianfun +#undef vl_typedefs + +/* define message structures */ +#define vl_typedefs +#include +#undef vl_typedefs + +/* + * Table of message reply handlers, must include boilerplate handlers + * we just generated + */ +#define foreach_memif_api_reply_msg \ + _(MEMIF_CREATE_REPLY, memif_create_reply) \ + _(MEMIF_DELETE_REPLY, memif_delete_reply) \ + _(MEMIF_DETAILS, memif_details) + +#define POINTER_MAP_SIZE 32 +static void *global_pointers_map[POINTER_MAP_SIZE]; +static uint8_t global_pointers_map_index = 0; + +uint32_t memif_binary_api_get_next_memif_id(vpp_plugin_binary_api_t *api) { + // Dump all the memif interfaces and return the next to the largest memif id + vl_api_memif_dump_t *mp; + vpp_plugin_binary_api_t *hm = api; + + M(MEMIF_DUMP, mp); + api->vpp_api->user_param = malloc(sizeof(uint32_t)); + *(uint32_t *)(api->vpp_api->user_param) = 0; + global_pointers_map[global_pointers_map_index] = api; + mp->context = global_pointers_map_index++; + global_pointers_map_index %= POINTER_MAP_SIZE; + + vpp_binary_api_send_request(api->vpp_api, mp); + + vpp_binary_api_send_receive_ping(api->vpp_api); + + uint32_t ret = *(uint32_t *)(api->vpp_api->user_param); + free(api->vpp_api->user_param); + + return ret; +} + +static void vl_api_memif_details_t_handler(vl_api_memif_details_t *mp) { + vpp_plugin_binary_api_t *binary_api = global_pointers_map[mp->context]; + uint32_t *last_memif_id = binary_api->vpp_api->user_param; + uint32_t current_memif_id = clib_net_to_host_u32(mp->id); + if (current_memif_id >= *last_memif_id) { + *last_memif_id = current_memif_id + 1; + } +} + +int memif_binary_api_create_memif(vpp_plugin_binary_api_t *api, + memif_create_params_t *input_params, + memif_output_params_t *output_params) { + vl_api_memif_create_t *mp; + vpp_plugin_binary_api_t *hm = api; + + if (input_params->socket_id == ~0) { + // invalid socket-id + return -1; + } + + if (!is_pow2(input_params->ring_size)) { + // ring size must be power of 2 + return -1; + } + + if (input_params->rx_queues > 255 || input_params->rx_queues < 1) { + // rx queue must be between 1 - 255 + return -1; + } + + if (input_params->tx_queues > 255 || input_params->tx_queues < 1) { + // tx queue must be between 1 - 255 + return -1; + } + + api->vpp_api->user_param = output_params; + + /* Construct the API message */ + M(MEMIF_CREATE, mp); + + global_pointers_map[global_pointers_map_index] = api; + mp->context = global_pointers_map_index++; + global_pointers_map_index %= POINTER_MAP_SIZE; + + mp->role = input_params->role; + mp->mode = input_params->mode; + mp->rx_queues = input_params->rx_queues; + mp->tx_queues = input_params->tx_queues; + mp->id = clib_host_to_net_u32(input_params->id); + mp->socket_id = clib_host_to_net_u32(input_params->socket_id); + mp->ring_size = clib_host_to_net_u32(input_params->ring_size); + mp->buffer_size = clib_host_to_net_u16(input_params->buffer_size); + + int ret = vpp_binary_api_send_request_wait_reply(api->vpp_api, mp); + if (ret < 0) { + return ret; + } + + return vpp_binary_api_set_int_state(api->vpp_api, output_params->sw_if_index, + UP); +} +int memif_binary_api_delete_memif(vpp_plugin_binary_api_t *api, + uint32_t sw_if_index) { + vl_api_memif_delete_t *mp; + vpp_plugin_binary_api_t *hm = api; + + /* Construct the API message */ + M(MEMIF_DELETE, mp); + + global_pointers_map[global_pointers_map_index] = api; + mp->context = global_pointers_map_index++; + global_pointers_map_index %= POINTER_MAP_SIZE; + + mp->sw_if_index = htonl(sw_if_index); + + return vpp_binary_api_send_request_wait_reply(api->vpp_api, mp); +} + +static void vl_api_memif_create_reply_t_handler( + vl_api_memif_create_reply_t *mp) { + vpp_plugin_binary_api_t *binary_api = global_pointers_map[mp->context]; + memif_output_params_t *params = binary_api->vpp_api->user_param; + + binary_api->vpp_api->ret_val = ntohl(mp->retval); + params->sw_if_index = clib_net_to_host_u32(mp->sw_if_index); + + TRANSPORT_LOGI("ret :%d", binary_api->vpp_api->ret_val); + + vpp_binary_api_unlock_waiting_thread(binary_api->vpp_api); +} + +static void vl_api_memif_delete_reply_t_handler( + vl_api_memif_delete_reply_t *mp) { + vpp_plugin_binary_api_t *binary_api = global_pointers_map[mp->context]; + + binary_api->vpp_api->ret_val = ntohl(mp->retval); + + vpp_binary_api_unlock_waiting_thread(binary_api->vpp_api); +} + +static int memif_binary_api_setup_handlers( + vpp_plugin_binary_api_t *binary_api) { + vpp_plugin_binary_api_t *sm __attribute__((unused)) = binary_api; +#define _(N, n) \ + vl_msg_api_set_handlers(VL_API_##N + sm->msg_id_base, #n, \ + vl_api_##n##_t_handler, vl_noop_handler, \ + vl_api_##n##_t_endian, vl_api_##n##_t_print, \ + sizeof(vl_api_##n##_t), 1); + foreach_memif_api_reply_msg; +#undef _ + return 0; +} + +vpp_plugin_binary_api_t *memif_binary_api_init(vpp_binary_api_t *api) { + vpp_plugin_binary_api_t *ret = malloc(sizeof(vpp_plugin_binary_api_t)); + u8 *name = format(0, "memif_%08x%c", api_version, 0); + ret->msg_id_base = vl_client_get_first_plugin_msg_id((char *)name); + ret->vpp_api = api; + ret->my_client_index = api->my_client_index; + memif_binary_api_setup_handlers(ret); + return ret; +} + +#endif // __vpp__ \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/memif_binary_api.h b/libtransport/src/hicn/transport/core/memif_binary_api.h new file mode 100755 index 000000000..582aeb1a0 --- /dev/null +++ b/libtransport/src/hicn/transport/core/memif_binary_api.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#ifdef __vpp__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "stdint.h" + +typedef struct memif_create_params_s { + uint8_t role; + uint8_t mode; + uint8_t rx_queues; + uint8_t tx_queues; + uint32_t id; + uint32_t socket_id; + uint8_t secret[24]; + uint32_t ring_size; + uint16_t buffer_size; + uint8_t hw_addr[6]; +} memif_create_params_t; + +typedef struct memif_output_params_s { + uint32_t sw_if_index; +} memif_output_params_t; + +vpp_plugin_binary_api_t* memif_binary_api_init(vpp_binary_api_t* api); + +uint32_t memif_binary_api_get_next_memif_id(vpp_plugin_binary_api_t* api); + +int memif_binary_api_create_memif(vpp_plugin_binary_api_t* api, + memif_create_params_t* input_params, + memif_output_params_t* output_params); + +int memif_binary_api_delete_memif(vpp_plugin_binary_api_t* api, + uint32_t sw_if_index); + +#ifdef __cplusplus +} +#endif + +#endif // __vpp__ \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/memif_connector.cc b/libtransport/src/hicn/transport/core/memif_connector.cc new file mode 100755 index 000000000..7a672314e --- /dev/null +++ b/libtransport/src/hicn/transport/core/memif_connector.cc @@ -0,0 +1,493 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#ifdef __vpp__ + +#include +#include + +#define CANCEL_TIMER 1 + +namespace transport { + +namespace core { + +std::once_flag MemifConnector::flag_; +utils::EpollEventReactor MemifConnector::main_event_reactor_; + +MemifConnector::MemifConnector(PacketReceivedCallback &&receive_callback, + OnReconnect &&on_reconnect_callback, + asio::io_service &io_service, + std::string app_name) + : Connector(), + memif_worker_(nullptr), + timer_set_(false), + send_timer_(std::make_unique(event_reactor_)), + io_service_(io_service), + work_(std::make_unique(io_service_)), + packet_counter_(0), + memif_connection_({}), + tx_buf_counter_(0), + is_connecting_(true), + is_reconnection_(false), + data_available_(false), + enable_burst_(false), + app_name_(app_name), + receive_callback_(receive_callback), + on_reconnect_callback_(on_reconnect_callback), + socket_filename_("") { + std::call_once(MemifConnector::flag_, &MemifConnector::init, this); +} + +MemifConnector::~MemifConnector() { close(); } + +void MemifConnector::init() { + /* initialize memory interface */ + int err = memif_init(controlFdUpdate, const_cast(app_name_.c_str()), + nullptr, nullptr, nullptr); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGI("memif_init: %s", memif_strerror(err)); + } +} + +void MemifConnector::connect(uint32_t memif_id, long memif_mode) { + TRANSPORT_LOGI("Creating memif"); + + memif_id_ = memif_id; + socket_filename_ = "/run/vpp/memif.sock"; + + createMemif(memif_id, memif_mode, nullptr); + + while (is_connecting_) { + MemifConnector::main_event_reactor_.runOneEvent(); + } + + int err; + + /* get interrupt queue id */ + int fd = -1; + err = memif_get_queue_efd(memif_connection_.conn, 0, &fd); + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGI("memif_get_queue_efd: %s", memif_strerror(err)); + return; + } + + // Remove fd from main epoll + main_event_reactor_.delFileDescriptor(fd); + + // Add fd to epoll of instance + event_reactor_.addFileDescriptor( + fd, EPOLLIN, [this](const utils::Event &evt) -> int { + return onInterrupt(memif_connection_.conn, this, 0); + }); + + memif_worker_ = std::make_unique( + std::bind(&MemifConnector::threadMain, this)); +} + +int MemifConnector::createMemif(uint32_t index, uint8_t mode, char *s) { + memif_connection_t *c = &memif_connection_; + + /* setting memif connection arguments */ + memif_conn_args_t args; + memset(&args, 0, sizeof(args)); + + args.is_master = mode; + args.log2_ring_size = MEMIF_LOG2_RING_SIZE; + args.buffer_size = MEMIF_BUF_SIZE; + args.num_s2m_rings = 1; + args.num_m2s_rings = 1; + strncpy((char *)args.interface_name, IF_NAME, strlen(IF_NAME)); + // strncpy((char *) args.instance_name, APP_NAME, strlen(APP_NAME)); + args.mode = memif_interface_mode_t::MEMIF_INTERFACE_MODE_IP; + args.socket_filename = (uint8_t *)socket_filename_.c_str(); + + TRANSPORT_LOGI("Socket filename: %s", args.socket_filename); + + args.interface_id = index; + /* last argument for memif_create (void * private_ctx) is used by user + to identify connection. this context is returned with callbacks */ + int err; + /* default interrupt */ + if (s == nullptr) { + err = memif_create(&c->conn, &args, onConnect, onDisconnect, onInterrupt, + this); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + throw errors::RuntimeException(memif_strerror(err)); + } + } + + c->index = (uint16_t)index; + c->tx_qid = 0; + /* alloc memif buffers */ + c->rx_buf_num = 0; + c->rx_bufs = static_cast( + malloc(sizeof(memif_buffer_t) * MAX_MEMIF_BUFS)); + c->tx_buf_num = 0; + c->tx_bufs = static_cast( + malloc(sizeof(memif_buffer_t) * MAX_MEMIF_BUFS)); + + // memif_set_rx_mode (c->conn, MEMIF_RX_MODE_POLLING, 0); + + return 0; +} + +int MemifConnector::deleteMemif() { + memif_connection_t *c = &memif_connection_; + + if (c->rx_bufs) { + free(c->rx_bufs); + } + + c->rx_bufs = nullptr; + c->rx_buf_num = 0; + + if (c->tx_bufs) { + free(c->tx_bufs); + } + + c->tx_bufs = nullptr; + c->tx_buf_num = 0; + + int err; + /* disconenct then delete memif connection */ + err = memif_delete(&c->conn); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGI("memif_delete: %s", memif_strerror(err)); + } + + if (TRANSPORT_EXPECT_FALSE(c->conn != nullptr)) { + TRANSPORT_LOGI("memif delete fail"); + } + + return 0; +} + +int MemifConnector::controlFdUpdate(int fd, uint8_t events) { + /* convert memif event definitions to epoll events */ + if (events & MEMIF_FD_EVENT_DEL) { + return MemifConnector::main_event_reactor_.delFileDescriptor(fd); + } + + uint32_t evt = 0; + + if (events & MEMIF_FD_EVENT_READ) { + evt |= EPOLLIN; + } + + if (events & MEMIF_FD_EVENT_WRITE) { + evt |= EPOLLOUT; + } + + if (events & MEMIF_FD_EVENT_MOD) { + return MemifConnector::main_event_reactor_.modFileDescriptor(fd, evt); + } + + return MemifConnector::main_event_reactor_.addFileDescriptor( + fd, evt, [](const utils::Event &evt) -> int { + uint32_t event = 0; + int memif_err = 0; + + if (evt.events & EPOLLIN) { + event |= MEMIF_FD_EVENT_READ; + } + + if (evt.events & EPOLLOUT) { + event |= MEMIF_FD_EVENT_WRITE; + } + + if (evt.events & EPOLLERR) { + event |= MEMIF_FD_EVENT_ERROR; + } + + memif_err = memif_control_fd_handler(evt.data.fd, event); + + if (TRANSPORT_EXPECT_FALSE(memif_err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGI("memif_control_fd_handler: %s", + memif_strerror(memif_err)); + } + + return 0; + }); +} + +int MemifConnector::bufferAlloc(long n, uint16_t qid) { + memif_connection_t *c = &memif_connection_; + int err; + uint16_t r; + /* set data pointer to shared memory and set buffer_len to shared mmeory + * buffer len */ + err = memif_buffer_alloc(c->conn, qid, c->tx_bufs, n, &r, 2000); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGD("memif_buffer_alloc: %s", memif_strerror(err)); + } + + c->tx_buf_num += r; + TRANSPORT_LOGD("allocated %d/%ld buffers, %u free buffers", r, n, + MAX_MEMIF_BUFS - c->tx_buf_num); + return r; +} + +int MemifConnector::txBurst(uint16_t qid) { + memif_connection_t *c = &memif_connection_; + int err; + uint16_t r; + /* inform peer memif interface about data in shared memory buffers */ + /* mark memif buffers as free */ + err = memif_tx_burst(c->conn, qid, c->tx_bufs, c->tx_buf_num, &r); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGI("memif_tx_burst: %s", memif_strerror(err)); + } + + // err = memif_refill_queue(c->conn, qid, r, 0); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGI("memif_tx_burst: %s", memif_strerror(err)); + c->tx_buf_num -= r; + return -1; + } + + TRANSPORT_LOGD("tx: %d/%u", r, c->tx_buf_num); + c->tx_buf_num -= r; + return 0; +} + +void MemifConnector::sendCallback(const std::error_code &ec) { + if (TRANSPORT_EXPECT_TRUE(!ec && !is_connecting_)) { + doSend(); + } + + if (output_buffer_.size() > 0) { + send_timer_->expiresFromNow(std::chrono::microseconds(50)); + send_timer_->asyncWait( + std::bind(&MemifConnector::sendCallback, this, std::placeholders::_1)); + } else { + timer_set_ = false; + } +} + +void MemifConnector::processInputBuffer() { + Packet::MemBufPtr ptr; + + while (input_buffer_.pop(ptr)) { + receive_callback_(std::move(ptr)); + } +} + +/* informs user about connected status. private_ctx is used by user to identify + connection (multiple connections WIP) */ +int MemifConnector::onConnect(memif_conn_handle_t conn, void *private_ctx) { + TRANSPORT_LOGI("memif connected!\n"); + MemifConnector *connector = (MemifConnector *)private_ctx; + memif_refill_queue(conn, 0, -1, 0); + connector->is_connecting_ = false; + + return 0; +} + +/* informs user about disconnected status. private_ctx is used by user to + identify connection (multiple connections WIP) */ +int MemifConnector::onDisconnect(memif_conn_handle_t conn, void *private_ctx) { + TRANSPORT_LOGI("memif disconnected!"); + MemifConnector *connector = (MemifConnector *)private_ctx; + // TRANSPORT_LOGI ("Packet received: %u", connector->packet_counter_); + TRANSPORT_LOGI("Packet to process: %u", + connector->memif_connection_.tx_buf_num); + return 0; +} + +void MemifConnector::threadMain() { event_reactor_.runEventLoop(1); } + +int MemifConnector::onInterrupt(memif_conn_handle_t conn, void *private_ctx, + uint16_t qid) { + MemifConnector *connector = (MemifConnector *)private_ctx; + + memif_connection_t *c = &connector->memif_connection_; + int err = MEMIF_ERR_SUCCESS, ret_val; + uint16_t rx; + + do { + err = memif_rx_burst(conn, qid, c->rx_bufs, MAX_MEMIF_BUFS, &rx); + ret_val = err; + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS && + err != MEMIF_ERR_NOBUF)) { + TRANSPORT_LOGI("memif_rx_burst: %s", memif_strerror(err)); + goto error; + } + + c->rx_buf_num += rx; + + if (TRANSPORT_EXPECT_TRUE(connector->io_service_.stopped())) { + TRANSPORT_LOGD("socket stopped: ignoring %u packets", rx); + goto error; + } + + for (int i = 0; i < rx; i++) { + auto packet = connector->getPacket(); + std::memcpy(packet->writableData(), + reinterpret_cast((c->rx_bufs + i)->data), + (c->rx_bufs + i)->len); + + if (!connector->input_buffer_.push(std::move(packet))) { + TRANSPORT_LOGI("Error pushing packet. Ring buffer full."); + + // TODO Here we should consider the possibility to signal the congestion + // to the application, that would react properly (e.g. slow down + // message) + } + } + + connector->io_service_.post( + std::bind(&MemifConnector::processInputBuffer, connector)); + + /* mark memif buffers and shared memory buffers as free */ + /* free processed buffers */ + + err = memif_refill_queue(conn, qid, rx, 0); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGI("memif_buffer_free: %s", memif_strerror(err)); + } + + c->rx_buf_num -= rx; + + TRANSPORT_LOGD("freed %d buffers. %u/%u alloc/free buffers", rx, rx, + MAX_MEMIF_BUFS - rx); + + // if (connector->enable_burst_) { + // connector->doSend(); + // } + } while (ret_val == MEMIF_ERR_NOBUF); + + return 0; + +error: + err = memif_refill_queue(c->conn, qid, rx, 0); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGI("memif_buffer_free: %s", memif_strerror(err)); + } + c->rx_buf_num -= rx; + + TRANSPORT_LOGD("freed %d buffers. %u/%u alloc/free buffers", rx, + c->rx_buf_num, MAX_MEMIF_BUFS - c->rx_buf_num); + return 0; +} + +// void MemifConnector::runEventsLoop() { +// io_service_.run(); +//} + +void MemifConnector::close() { + event_reactor_.stop(); + io_service_.stop(); + + if (memif_worker_ && memif_worker_->joinable()) { + memif_worker_->join(); + TRANSPORT_LOGD("Memif worker joined"); + deleteMemif(); + } else { + TRANSPORT_LOGD("Memif worker not joined"); + } +} + +void MemifConnector::enableBurst() { enable_burst_ = true; } + +void MemifConnector::send(const Packet::MemBufPtr &packet) { +#ifdef CANCEL_TIMER + if (!timer_set_) { + timer_set_ = true; + send_timer_->expiresFromNow(std::chrono::microseconds(50)); + send_timer_->asyncWait( + std::bind(&MemifConnector::sendCallback, this, std::placeholders::_1)); + } +#endif + + { + utils::SpinLock::Acquire locked(write_msgs_lock_); + output_buffer_.push_back(packet); + } +} + +int MemifConnector::doSend() { + std::size_t max = 0; + uint16_t n = 0; + std::size_t size = 0; + + { + utils::SpinLock::Acquire locked(write_msgs_lock_); + size = output_buffer_.size(); + } + + do { + max = size < MAX_MEMIF_BUFS ? size : MAX_MEMIF_BUFS; + + if (TRANSPORT_EXPECT_FALSE( + (n = bufferAlloc(max, memif_connection_.tx_qid)) < 0)) { + TRANSPORT_LOGI("Error allocating buffers."); + return -1; + } + + for (uint16_t i = 0; i < n; i++) { + utils::SpinLock::Acquire locked(write_msgs_lock_); + + auto packet = output_buffer_.front().get(); + const utils::MemBuf *current = packet; + std::size_t offset = 0; + uint8_t *shared_buffer = + reinterpret_cast(memif_connection_.tx_bufs[i].data); + do { + std::memcpy(shared_buffer + offset, current->data(), current->length()); + offset += current->length(); + current = current->next(); + } while (current != packet); + + memif_connection_.tx_bufs[i].len = uint32_t(offset); + + TRANSPORT_LOGD("Packet size : %zu", offset); + + output_buffer_.pop_front(); + } + + txBurst(memif_connection_.tx_qid); + + utils::SpinLock::Acquire locked(write_msgs_lock_); + size = output_buffer_.size(); + } while (size > 0); + + return 0; +} + +void MemifConnector::state() { + TRANSPORT_LOGD("Event reactor map: %zu", event_reactor_.mapSize()); + TRANSPORT_LOGD("Output buffer %zu", output_buffer_.size()); +} + +void MemifConnector::send(const uint8_t *packet, std::size_t len, + const PacketSentCallback &packet_sent) {} + +} // end namespace core + +} // end namespace transport + +#endif // __vpp__ \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/memif_connector.h b/libtransport/src/hicn/transport/core/memif_connector.h new file mode 100755 index 000000000..24c8ac16c --- /dev/null +++ b/libtransport/src/hicn/transport/core/memif_connector.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef __vpp__ + +#define _Static_assert static_assert + +extern "C" { +#include +}; + +namespace transport { + +namespace core { + +typedef struct { + uint16_t index; + /* memif conenction handle */ + memif_conn_handle_t conn; + /* transmit queue id */ + uint16_t tx_qid; + /* tx buffers */ + memif_buffer_t *tx_bufs; + /* allocated tx buffers counter */ + /* number of tx buffers pointing to shared memory */ + uint16_t tx_buf_num; + /* rx buffers */ + memif_buffer_t *rx_bufs; + /* allcoated rx buffers counter */ + /* number of rx buffers pointing to shared memory */ + uint16_t rx_buf_num; + /* interface ip address */ + uint8_t ip_addr[4]; +} memif_connection_t; + +#define APP_NAME "libtransport" +#define IF_NAME "vpp_connection" + +#define MAX_MEMIF_BUFS 1024 +#define MEMIF_BUF_SIZE 2048 +#define MEMIF_LOG2_RING_SIZE 11 + +class MemifConnector : public Connector { + public: + MemifConnector(PacketReceivedCallback &&receive_callback, + OnReconnect &&on_reconnect_callback, + asio::io_service &io_service, + std::string app_name = "Libtransport"); + + ~MemifConnector() override; + + void send(const Packet::MemBufPtr &packet) override; + + void send(const uint8_t *packet, std::size_t len, + const PacketSentCallback &packet_sent = 0) override; + + void close() override; + + void connect(uint32_t memif_id, long memif_mode); + + // void runEventsLoop(); + + void enableBurst() override; + + void state() override; + + TRANSPORT_ALWAYS_INLINE uint32_t getMemifId() { return memif_id_; }; + + private: + void init(); + + int doSend(); + + int createMemif(uint32_t index, uint8_t mode, char *s); + + uint32_t getMemifConfiguration(); + + int deleteMemif(); + + static int controlFdUpdate(int fd, uint8_t events); + + static int onConnect(memif_conn_handle_t conn, void *private_ctx); + + static int onDisconnect(memif_conn_handle_t conn, void *private_ctx); + + static int onInterrupt(memif_conn_handle_t conn, void *private_ctx, + uint16_t qid); + + void threadMain(); + + int txBurst(uint16_t qid); + + int bufferAlloc(long n, uint16_t qid); + + void sendCallback(const std::error_code &ec); + + void processInputBuffer(); + + private: + static utils::EpollEventReactor main_event_reactor_; + static std::unique_ptr main_worker_; + + int epfd; + std::unique_ptr memif_worker_; + utils::EpollEventReactor event_reactor_; + volatile bool timer_set_; + std::unique_ptr send_timer_; + asio::io_service &io_service_; + std::unique_ptr work_; + uint32_t packet_counter_; + memif_connection_t memif_connection_; + uint16_t tx_buf_counter_; + + PacketRing input_buffer_; + volatile bool is_connecting_; + volatile bool is_reconnection_; + bool data_available_; + bool enable_burst_; + uint32_t memif_id_; + uint8_t memif_mode_; + std::string app_name_; + uint16_t transmission_index_; + PacketReceivedCallback receive_callback_; + OnReconnect on_reconnect_callback_; + utils::SpinLock write_msgs_lock_; + std::string socket_filename_; + + static std::once_flag flag_; +}; + +} // end namespace core + +} // end namespace transport + +#endif // __vpp__ \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/name.cc b/libtransport/src/hicn/transport/core/name.cc new file mode 100755 index 000000000..10c45eb08 --- /dev/null +++ b/libtransport/src/hicn/transport/core/name.cc @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +namespace transport { + +namespace core { + +Name::Name() { name_ = createEmptyName(); } + +Name::Name(int family, const uint8_t *ip_address, std::uint32_t suffix) + : name_(createEmptyName()) { + std::size_t length; + uint8_t *dst = NULL; + + if (family == AF_INET) { + dst = name_->ip4.prefix_as_u8; + length = IPV4_ADDR_LEN; + name_->type = HNT_CONTIGUOUS_V4; + } else if (family == AF_INET6) { + dst = name_->ip6.prefix_as_u8; + length = IPV6_ADDR_LEN; + name_->type = HNT_CONTIGUOUS_V6; + } else { + throw errors::RuntimeException("Specified name family does not exist."); + } + + std::memcpy(dst, ip_address, length); + *reinterpret_cast(dst + length) = suffix; +} + +Name::Name(const char *name, uint32_t segment) { + name_ = createEmptyName(); + + if (hicn_name_create(name, segment, name_.get()) < 0) { + throw errors::InvalidIpAddressException(); + } +} + +Name::Name(const std::string &uri, uint32_t segment) + : Name(uri.c_str(), segment) {} + +Name::Name(const std::string &uri) { + utils::StringTokenizer tokenizer(uri, "|"); + std::string ip_address; + std::string seq_number; + + ip_address = tokenizer.nextToken(); + + try { + seq_number = tokenizer.nextToken(); + } catch (errors::TokenizerException &e) { + seq_number = "0"; + } + + name_ = createEmptyName(); + + if (hicn_name_create(ip_address.c_str(), (uint32_t)atoi(seq_number.c_str()), + name_.get()) < 0) { + throw errors::InvalidIpAddressException(); + } +} + +Name::Name(const Name &name, bool hard_copy) { + name_ = createEmptyName(); + + if (hard_copy) { + if (hicn_name_copy(this->name_.get(), name.name_.get()) < 0) { + throw errors::MalformedNameException(); + } + } else { + *this->name_ = *name.name_; + } +} + +Name::Name(Name &&name) : name_(std::move(name.name_)) {} + +Name &Name::operator=(const Name &name) { + if (hicn_name_copy(this->name_.get(), name.name_.get()) < 0) { + throw errors::MalformedNameException(); + } + + return *this; +} + +bool Name::operator==(const Name &name) const { + return this->equals(name, true); +} + +bool Name::operator!=(const Name &name) const { + return !this->operator==(name); +} + +Name::operator bool() const { + return bool(hicn_name_empty((hicn_name_t *)name_.get())); +} + +bool Name::equals(const Name &name, bool consider_segment) const { + return !hicn_name_compare(name_.get(), name.name_.get(), consider_segment); +} + +std::string Name::toString() const { + char *name = new char[100]; + int ret = hicn_name_ntop(name_.get(), name, standard_name_string_length); + if (ret < 0) { + throw errors::MalformedNameException(); + } + std::string name_string(name); + delete[] name; + + return name_string; +} + +uint32_t Name::getHash32() const { + uint32_t hash; + if (hicn_name_hash((hicn_name_t *)name_.get(), &hash) < 0) { + throw errors::RuntimeException("Error computing the hash of the name!"); + } + return hash; +} + +void Name::clear() { + name_.reset(); + name_ = createEmptyName(); +}; + +Name::Type Name::getType() const { return name_->type; } + +uint32_t Name::getSuffix() const { + uint32_t ret = 0; + if (hicn_name_get_seq_number((hicn_name_t *)name_.get(), &ret) < 0) { + throw errors::RuntimeException( + "Impossible to retrieve the sequence number from the name."); + } + return ret; +} + +Name &Name::setSuffix(uint32_t seq_number) { + if (hicn_name_set_seq_number(name_.get(), seq_number) < 0) { + throw errors::RuntimeException( + "Impossible to set the sequence number to the name."); + } + + return *this; +} + +std::shared_ptr Name::getAddress() const { + Sockaddr *ret = nullptr; + + switch (name_->type) { + case HNT_CONTIGUOUS_V4: + case HNT_IOV_V4: + ret = (Sockaddr *)new Sockaddr4; + break; + case HNT_CONTIGUOUS_V6: + case HNT_IOV_V6: + ret = (Sockaddr *)new Sockaddr6; + break; + default: + throw errors::MalformedNameException(); + } + + if (hicn_name_to_sockaddr_address((hicn_name_t *)name_.get(), ret) < 0) { + throw errors::MalformedNameException(); + } + + return std::shared_ptr(ret); +} + +ip_address_t Name::toIpAddress() const { + ip_address_t ret; + std::memset(&ret, 0, sizeof(ret)); + + if (hicn_name_to_ip_address(name_.get(), &ret) < 0) { + throw errors::InvalidIpAddressException(); + } + + return ret; +} + +int Name::getAddressFamily() const { + int ret = 0; + + if (hicn_name_get_family(name_.get(), &ret) < 0) { + throw errors::InvalidIpAddressException(); + } + + return ret; +} + +void Name::copyToDestination(uint8_t *destination, bool include_suffix) const { + if (hicn_name_copy_to_destination(destination, name_.get(), include_suffix) < + 0) { + throw errors::RuntimeException( + "Impossibe to copy the name into the " + "provided destination"); + } +} + +std::ostream &operator<<(std::ostream &os, const Name &name) { + const std::string &str = name.toString(); + // os << "core:/"; + os << str; + + return os; +} + +} // end namespace core + +} // end namespace transport + +namespace std { +size_t hash::operator()( + const transport::core::Name &name) const { + return name.getHash32(); +} + +} // end namespace std diff --git a/libtransport/src/hicn/transport/core/name.h b/libtransport/src/hicn/transport/core/name.h new file mode 100755 index 000000000..b0da15026 --- /dev/null +++ b/libtransport/src/hicn/transport/core/name.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include + +extern "C" { +TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat") +#include +}; + +#include + +namespace transport { + +namespace core { + +typedef struct sockaddr_in6 Sockaddr6; +typedef struct sockaddr_in Sockaddr4; +typedef struct sockaddr Sockaddr; + +enum class HashAlgorithm : uint8_t; + +class Name { + friend class Packet; + friend class ContentObject; + friend class Interest; + + static const uint32_t standard_name_string_length = 100; + + public: + using NameStruct = hicn_name_t; + using Type = hicn_name_type_t; + + Name(); + + /** + * @brief Create name + * @param name The null-terminated URI string + */ + Name(const char *name, uint32_t segment); + + Name(int family, const uint8_t *ip_address, std::uint32_t suffix = 0); + + Name(const std::string &uri, uint32_t segment); + + Name(const std::string &uri); + + Name(const Name &name, bool hard_copy = false); + + Name(Name &&name); + + Name &operator=(const Name &name); + + bool operator==(const Name &name) const; + + bool operator!=(const Name &name) const; + + operator bool() const; + + std::string toString() const; + + bool equals(const Name &name, bool consider_segment = true) const; + + uint32_t getHash32() const; + + void clear(); + + Type getType() const; + + uint32_t getSuffix() const; + + std::shared_ptr getAddress() const; + + Name &setSuffix(uint32_t seq_number); + + ip_address_t toIpAddress() const; + + void copyToDestination(uint8_t *destination, + bool include_suffix = false) const; + + int getAddressFamily() const; + + private: + TRANSPORT_ALWAYS_INLINE NameStruct *getStructReference() const { + if (TRANSPORT_EXPECT_TRUE(name_ != nullptr)) { + return name_.get(); + } + + return nullptr; + } + + static TRANSPORT_ALWAYS_INLINE std::unique_ptr createEmptyName() { + NameStruct *name = new NameStruct; + name->type = HNT_UNSPEC; + return std::unique_ptr(name); + }; + + std::unique_ptr name_; +}; + +std::ostream &operator<<(std::ostream &os, const Name &name); + +} // end namespace core + +} // end namespace transport + +namespace std { +template <> +struct hash { + size_t operator()(const transport::core::Name &name) const; +}; + +} // end namespace std diff --git a/libtransport/src/hicn/transport/core/packet.cc b/libtransport/src/hicn/transport/core/packet.cc new file mode 100755 index 000000000..74f407ff7 --- /dev/null +++ b/libtransport/src/hicn/transport/core/packet.cc @@ -0,0 +1,614 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +extern "C" { +TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat") +#include +} + +namespace transport { + +namespace core { + +const core::Name Packet::base_name("0::0|0"); + +Packet::Packet(Format format) + : packet_(utils::MemBuf::create(getHeaderSizeFromFormat(format)).release()), + packet_start_(packet_->writableData()), + header_head_(packet_.get()), + payload_head_(nullptr), + format_(format) { + if (hicn_packet_init_header(format, (hicn_header_t *)packet_start_) < 0) { + throw errors::RuntimeException("Unexpected error initializing the packet."); + } + + packet_->append(getHeaderSizeFromFormat(format_)); +} + +Packet::Packet(MemBufPtr &&buffer) + : packet_(std::move(buffer)), + packet_start_(packet_->writableData()), + header_head_(packet_.get()), + payload_head_(nullptr), + format_(getFormatFromBuffer(packet_start_)) { + + auto header_size = getHeaderSizeFromFormat(format_); + int signature_size = 0; + + if (_is_ah(format_)) { + signature_size = getSignatureSize(); + } + + auto payload_length = packet_->length() - header_size - signature_size; + + if (!payload_length && !signature_size) { + return; + } + + packet_->trimEnd(packet_->length()); + + if (signature_size) { + auto sig = packet_->cloneOne(); + sig->advance(header_size); + sig->append(signature_size); + packet_->appendChain(std::move(sig)); + } + + if (payload_length) { + auto payload = packet_->cloneOne(); + payload_head_ = payload.get(); + payload_head_->advance(header_size + signature_size); + payload_head_->append(payload_length); + packet_->prependChain(std::move(payload)); + packet_->append(header_size); + } + + +} + +Packet::Packet(const uint8_t *buffer, std::size_t size) + : Packet(MemBufPtr(utils::MemBuf::copyBuffer(buffer, size).release())) {} + +Packet::Packet(Packet &&other) + : packet_(std::move(other.packet_)), + packet_start_(packet_->writableData()), + header_head_(other.header_head_), + payload_head_(other.payload_head_), + format_(other.format_) { + other.packet_start_ = nullptr; + other.header_head_ = nullptr; + other.payload_head_ = nullptr; + other.format_ = HF_UNSPEC; +} + +Packet::~Packet() { + if (packet_->isChained()) { + packet_->separateChain(packet_->next(), packet_->prev()); + } +} + +std::size_t Packet::getHeaderSizeFromFormat(Format format, + size_t signature_size) { + std::size_t header_length; + hicn_packet_get_header_length_from_format(format, &header_length); + int is_ah = _is_ah(format); + return is_ah * (header_length + signature_size) + (!is_ah) * header_length; +} + +std::size_t Packet::getHeaderSizeFromBuffer(Format format, + const uint8_t *buffer) { + size_t header_length; + if (hicn_packet_get_header_length(format, (hicn_header_t *)buffer, + &header_length) < 0) { + throw errors::MalformedPacketException(); + } + return header_length; +} + +bool Packet::isInterest(const uint8_t *buffer) { + bool is_interest = false; + + if (TRANSPORT_EXPECT_FALSE(hicn_packet_test_ece((const hicn_header_t *)buffer, + &is_interest) < 0)) { + throw errors::RuntimeException( + "Impossible to retrieve ece flag from packet"); + } + + return !is_interest; +} + +Packet::Format Packet::getFormatFromBuffer(const uint8_t *buffer) { + Format format = HF_UNSPEC; + + if (TRANSPORT_EXPECT_FALSE( + hicn_packet_get_format((const hicn_header_t *)buffer, &format) < 0)) { + throw errors::MalformedPacketException(); + } + + return format; +} + +std::size_t Packet::getPayloadSizeFromBuffer(Format format, + const uint8_t *buffer) { + std::size_t payload_length; + if (TRANSPORT_EXPECT_FALSE( + hicn_packet_get_payload_length(format, (hicn_header_t *)buffer, + &payload_length) < 0)) { + throw errors::MalformedPacketException(); + } + + return payload_length; +} + +std::size_t Packet::payloadSize() const { + return getPayloadSizeFromBuffer(format_, packet_start_); +} + +std::size_t Packet::headerSize() const { + return getHeaderSizeFromBuffer(format_, packet_start_); +} + +const uint8_t *Packet::start() const { return packet_start_; } + +void Packet::setLifetime(uint32_t lifetime) { + if (hicn_interest_set_lifetime((hicn_header_t *)packet_start_, lifetime) < + 0) { + throw errors::MalformedPacketException(); + } +} + +uint32_t Packet::getLifetime() const { + uint32_t lifetime = 0; + + if (hicn_packet_get_lifetime((hicn_header_t *)packet_start_, &lifetime) < 0) { + throw errors::MalformedPacketException(); + } + + return lifetime; +} + +Packet &Packet::appendPayload(std::unique_ptr &&payload) { + if (!payload_head_) { + payload_head_ = payload.get(); + } + + header_head_->prependChain(std::move(payload)); + updateLength(); + return *this; +} + +Packet &Packet::appendPayload(const uint8_t *buffer, std::size_t length) { + return appendPayload(utils::MemBuf::copyBuffer(buffer, length)); +} + +Packet &Packet::appendHeader(std::unique_ptr &&header) { + if (!payload_head_) { + header_head_->prependChain(std::move(header)); + } else { + payload_head_->prependChain(std::move(header)); + } + + updateLength(); + return *this; +} + +Packet &Packet::appendHeader(const uint8_t *buffer, std::size_t length) { + return appendHeader(utils::MemBuf::copyBuffer(buffer, length)); +} + +utils::Array Packet::getPayload() const { + if (TRANSPORT_EXPECT_FALSE(payload_head_ == nullptr)) { + return utils::Array(); + } + + // Hopefully the payload is contiguous + if (TRANSPORT_EXPECT_FALSE(payload_head_->next() != header_head_)) { + payload_head_->gather(payloadSize()); + } + + return utils::Array(payload_head_->writableData(), + payload_head_->length()); +} + +Packet &Packet::updateLength(std::size_t length) { + std::size_t total_length = length; + + for (utils::MemBuf *current = payload_head_; + current && current != header_head_; current = current->next()) { + total_length += current->length(); + } + + if (hicn_packet_set_payload_length(format_, (hicn_header_t *)packet_start_, + total_length) < 0) { + throw errors::RuntimeException("Error setting the packet payload."); + } + + return *this; +} + +PayloadType Packet::getPayloadType() const { + hicn_payload_type_t ret = HPT_UNSPEC; + + if (hicn_packet_get_payload_type((hicn_header_t *)packet_start_, &ret) < 0) { + throw errors::RuntimeException("Impossible to retrieve payload type."); + } + + return PayloadType(ret); +} + +Packet &Packet::setPayloadType(PayloadType payload_type) { + if (hicn_packet_set_payload_type((hicn_header_t *)packet_start_, + hicn_payload_type_t(payload_type)) < 0) { + throw errors::RuntimeException("Error setting payload type of the packet."); + } + + return *this; +} + +Packet::Format Packet::getFormat() const { + if (format_ == HF_UNSPEC) { + if (hicn_packet_get_format((hicn_header_t *)packet_start_, &format_) < 0) { + throw errors::MalformedPacketException(); + } + } + + return format_; +} + +const std::shared_ptr Packet::data() { return packet_; } + +void Packet::dump() const { + TRANSPORT_LOGI("The header length is: %zu", headerSize()); + TRANSPORT_LOGI("The payload length is: %zu", payloadSize()); + std::cerr << std::endl; + + hicn_packet_dump((uint8_t *)packet_->data(), headerSize()); + // hicn_packet_dump((uint8_t *)packet_->next()->data(), payloadSize()); +} + +void Packet::setSignatureSize(std::size_t size_bytes) { + int ret = hicn_packet_set_signature_size( + format_, (hicn_header_t *)packet_start_, size_bytes); + + if (ret < 0) { + throw errors::RuntimeException("Packet without Authentication Header."); + } +} + +std::size_t Packet::getSignatureSize() const { + size_t size_bytes; + int ret = hicn_packet_get_signature_size( + format_, (hicn_header_t *)packet_start_, &size_bytes); + + if (ret < 0) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + return size_bytes; +} + +void Packet::setSignature(std::unique_ptr &&signature) { + // Check if packet already contains a signature + auto header = header_head_->next(); + while (header != payload_head_) { + header->unlink(); + header = header->next(); + } + + appendHeader(std::move(signature)); +} + +void Packet::setSignatureTimestamp(const uint64_t ×tamp) { + int ret = hicn_packet_set_signature_timestamp( + format_, (hicn_header_t *)packet_start_, timestamp); + + if (ret < 0) { + throw errors::RuntimeException("Error setting the signature timestamp."); + } +} + +uint64_t Packet::getSignatureTimestamp() const { + uint64_t return_value; + int ret = hicn_packet_get_signature_timestamp( + format_, (hicn_header_t *)packet_start_, &return_value); + + if (ret < 0) { + throw errors::RuntimeException("Error getting the signature timestamp."); + } + + return return_value; +} + +void Packet::setValidationAlgorithm(const utils::CryptoSuite &validation_algorithm) { + int ret = hicn_packet_set_validation_algorithm( + format_, (hicn_header_t *)packet_start_, uint8_t(validation_algorithm)); + + if (ret < 0) { + throw errors::RuntimeException("Error setting the validation algorithm."); + } +} + +utils::CryptoSuite Packet::getValidationAlgorithm() const { + uint8_t return_value; + int ret = hicn_packet_get_validation_algorithm( + format_, (hicn_header_t *)packet_start_, &return_value); + + if (ret < 0) { + throw errors::RuntimeException("Error getting the validation algorithm."); + } + + return utils::CryptoSuite(return_value); +} + +void Packet::setKeyId(const utils::KeyId &key_id) { + int ret = hicn_packet_set_key_id( + format_, (hicn_header_t *)packet_start_, key_id.first); + + if (ret < 0) { + throw errors::RuntimeException("Error setting the key id."); + } +} + +utils::KeyId Packet::getKeyId() const { + utils::KeyId return_value; + int ret = hicn_packet_get_key_id( + format_, (hicn_header_t *)packet_start_, &return_value.first, &return_value.second); + + if (ret < 0) { + throw errors::RuntimeException("Error getting the validation algorithm."); + } + + return return_value; +} + +utils::CryptoHash Packet::computeDigest(HashAlgorithm algorithm) const { + utils::CryptoHasher hasher(static_cast(algorithm)); + hasher.init(); + + // Copy IP+TCP/ICMP header before zeroing them + hicn_header_t header_copy; + + hicn_packet_copy_header(format_, (hicn_header_t *)packet_start_, &header_copy, + false); + + const_cast(this)->resetForHash(); + + std::size_t payload_len = getPayloadSizeFromBuffer(format_, packet_start_); + std::size_t header_length = getHeaderSizeFromFormat(format_); + std::size_t signature_size = _is_ah(format_) ? getSignatureSize() : 0; + + hasher.updateBytes(packet_start_, + payload_len + header_length + signature_size); + + hicn_packet_copy_header(format_, &header_copy, (hicn_header_t *)packet_start_, + false); + + return hasher.finalize(); +} + +void Packet::setChecksum() { + uint16_t partial_csum = 0; + + for (utils::MemBuf *current = header_head_->next(); + current && current != header_head_; current = current->next()) { + if (partial_csum != 0) { + partial_csum = ~partial_csum; + } + partial_csum = csum(current->data(), current->length(), partial_csum); + } + if (hicn_packet_compute_header_checksum( + format_, (hicn_header_t *)packet_start_, partial_csum) < 0) { + throw errors::MalformedPacketException(); + } +} + +bool Packet::checkIntegrity() const { + if (hicn_packet_check_integrity(format_, (hicn_header_t *)packet_start_) < + 0) { + return false; + } + + return true; +} + +Packet &Packet::setSyn() { + if (hicn_packet_set_syn((hicn_header_t *)packet_start_) < 0) { + throw errors::RuntimeException("Error setting syn bit in the packet."); + } + + return *this; +} + +Packet &Packet::resetSyn() { + if (hicn_packet_reset_syn((hicn_header_t *)packet_start_) < 0) { + throw errors::RuntimeException("Error resetting syn bit in the packet."); + } + + return *this; +} + +bool Packet::testSyn() const { + bool res = false; + if (hicn_packet_test_syn((hicn_header_t *)packet_start_, &res) < 0) { + throw errors::RuntimeException("Error testing syn bit in the packet."); + } + + return res; +} + +Packet &Packet::setAck() { + if (hicn_packet_set_ack((hicn_header_t *)packet_start_) < 0) { + throw errors::RuntimeException("Error setting ack bit in the packet."); + } + + return *this; +} + +Packet &Packet::resetAck() { + if (hicn_packet_reset_ack((hicn_header_t *)packet_start_) < 0) { + throw errors::RuntimeException("Error resetting ack bit in the packet."); + } + + return *this; +} + +bool Packet::testAck() const { + bool res = false; + if (hicn_packet_test_ack((hicn_header_t *)packet_start_, &res) < 0) { + throw errors::RuntimeException("Error testing ack bit in the packet."); + } + + return res; +} + +Packet &Packet::setRst() { + if (hicn_packet_set_rst((hicn_header_t *)packet_start_) < 0) { + throw errors::RuntimeException("Error setting rst bit in the packet."); + } + + return *this; +} + +Packet &Packet::resetRst() { + if (hicn_packet_reset_rst((hicn_header_t *)packet_start_) < 0) { + throw errors::RuntimeException("Error resetting rst bit in the packet."); + } + + return *this; +} + +bool Packet::testRst() const { + bool res = false; + if (hicn_packet_test_rst((hicn_header_t *)packet_start_, &res) < 0) { + throw errors::RuntimeException("Error testing rst bit in the packet."); + } + + return res; +} + +Packet &Packet::setFin() { + if (hicn_packet_set_fin((hicn_header_t *)packet_start_) < 0) { + throw errors::RuntimeException("Error setting fin bit in the packet."); + } + + return *this; +} + +Packet &Packet::resetFin() { + if (hicn_packet_reset_fin((hicn_header_t *)packet_start_) < 0) { + throw errors::RuntimeException("Error resetting fin bit in the packet."); + } + + return *this; +} + +bool Packet::testFin() const { + bool res = false; + if (hicn_packet_test_fin((hicn_header_t *)packet_start_, &res) < 0) { + throw errors::RuntimeException("Error testing fin bit in the packet."); + } + + return res; +} + +Packet &Packet::resetFlags() { + resetSyn(); + resetAck(); + resetRst(); + resetFin(); + + return *this; +} + +std::string Packet::printFlags() const { + std::string flags = ""; + if (testSyn()) { + flags += "S"; + } + if (testAck()) { + flags += "A"; + } + if (testRst()) { + flags += "R"; + } + if (testFin()) { + flags += "F"; + } + return flags; +} + +Packet &Packet::setSrcPort(uint16_t srcPort) { + if (hicn_packet_set_src_port((hicn_header_t *)packet_start_, srcPort) < 0) { + throw errors::RuntimeException("Error setting source port in the packet."); + } + + return *this; +} + +Packet &Packet::setDstPort(uint16_t dstPort) { + if (hicn_packet_set_dst_port((hicn_header_t *)packet_start_, dstPort) < 0) { + throw errors::RuntimeException( + "Error setting destination port in the packet."); + } + + return *this; +} + +uint16_t Packet::getSrcPort() const { + uint16_t port = 0; + + if (hicn_packet_get_src_port((hicn_header_t *)packet_start_, &port) < 0) { + throw errors::RuntimeException("Error reading source port in the packet."); + } + + return port; +} + +uint16_t Packet::getDstPort() const { + uint16_t port = 0; + + if (hicn_packet_get_dst_port((hicn_header_t *)packet_start_, &port) < 0) { + throw errors::RuntimeException( + "Error reading destination port in the packet."); + } + + return port; +} + +Packet &Packet::setTTL(uint8_t hops) { + if (hicn_packet_set_hoplimit((hicn_header_t *)packet_start_, hops) < 0) { + throw errors::RuntimeException("Error setting TTL."); + } + + return *this; +} + +uint8_t Packet::getTTL() const { + uint8_t hops = 0; + if (hicn_packet_get_hoplimit((hicn_header_t *)packet_start_, &hops) < 0) { + throw errors::RuntimeException("Error reading TTL."); + } + + return hops; +} + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/packet.h b/libtransport/src/hicn/transport/core/packet.h new file mode 100755 index 000000000..0a5673401 --- /dev/null +++ b/libtransport/src/hicn/transport/core/packet.h @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace utils { +class Signer; +class Verifier; +} // namespace utils + +namespace transport { + +namespace core { + +/* + * Basic IP packet, modelled as circular chain of buffers: + * Header = H + * Payload = P + * + * H_0 --> H_1 --> H_2 --> P_0 --> P_1 --> P_2 + * \_______________________________________| + */ + +class Packet : public std::enable_shared_from_this { + friend class utils::Signer; + friend class utils::Verifier; + + public: + using MemBufPtr = std::shared_ptr; + using Format = hicn_format_t; + static constexpr size_t default_mtu = 1500; + + /** + * Create new IP packet. Here we allocate just the header, + * the eventual payload will be added by prepending the payload buffer + * to the buffer chain whose the fist buffer is the header itself. + */ + Packet(Format format = HF_UNSPEC); + + /** + * Create new IP packet using raw buffer. + */ + Packet(const uint8_t *buffer, std::size_t size); + Packet(MemBufPtr &&buffer); + + /* + * Enforce zero-copy lifestyle. + */ + Packet(const Packet &other) = delete; + Packet &operator=(const Packet &other) = delete; + + /* + * Move constructor. + */ + Packet(Packet &&other); + + friend bool operator==(const Packet &l_packet, const Packet &r_packet); + + virtual ~Packet(); + + static std::size_t getHeaderSizeFromFormat(Format format, + std::size_t signature_size = 0); + + static std::size_t getHeaderSizeFromBuffer(Format format, + const uint8_t *buffer); + + static std::size_t getPayloadSizeFromBuffer(Format format, + const uint8_t *buffer); + + static bool isInterest(const uint8_t *buffer); + + static Format getFormatFromBuffer(const uint8_t *buffer); + + std::size_t payloadSize() const; + + std::size_t headerSize() const; + + const std::shared_ptr data(); + + const uint8_t *start() const; + + virtual void setLifetime(uint32_t lifetime); + + virtual uint32_t getLifetime() const; + + Packet &appendPayload(const uint8_t *buffer, std::size_t length); + + Packet &appendPayload(std::unique_ptr &&payload); + + Packet &appendHeader(std::unique_ptr &&header); + + Packet &appendHeader(const uint8_t *buffer, std::size_t length); + + utils::Array getPayload() const; + + Packet &updateLength(std::size_t length = 0); + + PayloadType getPayloadType() const; + + Packet &setPayloadType(PayloadType payload_type); + + Format getFormat() const; + + void dump() const; + + virtual void setLocator(const ip_address_t &locator) = 0; + + virtual ip_address_t getLocator() const = 0; + + void setSignatureSize(std::size_t size_bytes); + + std::size_t getSignatureSize() const; + + void setSignatureTimestamp(const uint64_t ×tamp); + + uint64_t getSignatureTimestamp() const; + + void setValidationAlgorithm(const utils::CryptoSuite &validation_algorithm); + + utils::CryptoSuite getValidationAlgorithm() const; + + void setKeyId(const utils::KeyId &key_id); + + utils::KeyId getKeyId() const; + + void setSignature(std::unique_ptr &&signature); + + virtual utils::CryptoHash computeDigest(HashAlgorithm algorithm) const; + + void setChecksum(); + + bool checkIntegrity() const; + + Packet &setSyn(); + Packet &resetSyn(); + bool testSyn() const; + Packet &setAck(); + Packet &resetAck(); + bool testAck() const; + Packet &setRst(); + Packet &resetRst(); + bool testRst() const; + Packet &setFin(); + Packet &resetFin(); + bool testFin() const; + Packet &resetFlags(); + std::string printFlags() const; + + Packet &setSrcPort(uint16_t srcPort); + Packet &setDstPort(uint16_t dstPort); + uint16_t getSrcPort() const; + uint16_t getDstPort() const; + + Packet &setTTL(uint8_t hops); + uint8_t getTTL() const; + + private: + virtual void resetForHash() = 0; + + protected: + Name name_; + MemBufPtr packet_; + uint8_t *packet_start_; + utils::MemBuf *header_head_; + utils::MemBuf *payload_head_; + mutable Format format_; + + static const core::Name base_name; +}; + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/payload_type.h b/libtransport/src/hicn/transport/core/payload_type.h new file mode 100755 index 000000000..fa79db35a --- /dev/null +++ b/libtransport/src/hicn/transport/core/payload_type.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +namespace transport { + +namespace core { + +enum class PayloadType : uint16_t { + CONTENT_OBJECT = HPT_DATA, + MANIFEST = HPT_MANIFEST, +}; + +} // end namespace core + +} // end namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/pending_interest.cc b/libtransport/src/hicn/transport/core/pending_interest.cc new file mode 100755 index 000000000..8f6de1839 --- /dev/null +++ b/libtransport/src/hicn/transport/core/pending_interest.cc @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +namespace transport { + +namespace core { + +PendingInterest::PendingInterest() + : interest_(nullptr, nullptr), timer_(), received_(false) {} + +PendingInterest::PendingInterest(Interest::Ptr &&interest, + std::unique_ptr &&timer) + : interest_(std::move(interest)), + timer_(std::move(timer)), + received_(false) {} + +PendingInterest::~PendingInterest() { + // timer_.reset(); +} + +void PendingInterest::cancelTimer() { timer_->cancel(); } + +bool PendingInterest::isReceived() const { return received_; } + +void PendingInterest::setReceived() { received_ = true; } + +Interest::Ptr &&PendingInterest::getInterest() { return std::move(interest_); } + +void PendingInterest::setReceived(bool received) { + PendingInterest::received_ = received; +} + +} // end namespace core + +} // end namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/pending_interest.h b/libtransport/src/hicn/transport/core/pending_interest.h new file mode 100755 index 000000000..cbcafb5d9 --- /dev/null +++ b/libtransport/src/hicn/transport/core/pending_interest.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace transport { + +namespace core { + +class HicnForwarderInterface; +class VPPForwarderInterface; +class RawSocketInterface; + +template +class Portal; + +typedef std::function TimerCallback; + +class PendingInterest { + friend class Portal; + friend class Portal; + friend class Portal; + + public: + PendingInterest(); + + PendingInterest(Interest::Ptr &&interest, + std::unique_ptr &&timer); + + ~PendingInterest(); + + bool isReceived() const; + + template + TRANSPORT_ALWAYS_INLINE void startCountdown(Handler &&cb) { + timer_->expires_from_now( + std::chrono::milliseconds(interest_->getLifetime())); + timer_->async_wait(cb); + } + + void cancelTimer(); + + void setReceived(); + + Interest::Ptr &&getInterest(); + + void setReceived(bool received); + + bool isValid() const; + + void setValid(bool valid); + + private: + Interest::Ptr interest_; + std::unique_ptr timer_; + bool received_; +}; + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/portal.h b/libtransport/src/hicn/transport/core/portal.h new file mode 100755 index 000000000..88020447f --- /dev/null +++ b/libtransport/src/hicn/transport/core/portal.h @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __vpp__ +#include +#endif + +#include +#include +#include +#include +#include + +#define UNSET_CALLBACK 0 + +namespace transport { + +namespace core { + +typedef std::unordered_map> + PendingInterestHashTable; + +template +class BasicBindConfig { + static_assert(std::is_same::value, + "Prefix must be a Prefix type."); + + const uint32_t standard_cs_reserved = 5000; + + public: + template + BasicBindConfig(T &&prefix) + : prefix_(std::forward(prefix)), + content_store_reserved_(standard_cs_reserved) {} + + template + BasicBindConfig(T &&prefix, uint32_t cs_reserved) + : prefix_(std::forward(prefix)), + content_store_reserved_(cs_reserved) {} + + TRANSPORT_ALWAYS_INLINE const PrefixType &prefix() const { return prefix_; } + + TRANSPORT_ALWAYS_INLINE uint32_t csReserved() const { + return content_store_reserved_; + } + + private: + PrefixType prefix_; + uint32_t content_store_reserved_; +}; + +using BindConfig = BasicBindConfig; + +template +class Portal { + static_assert( + std::is_base_of, + ForwarderInt>::value, + "ForwarderInt must inherit from ForwarderInterface!"); + + public: + class ConsumerCallback { + public: + virtual void onContentObject(Interest::Ptr &&i, ContentObject::Ptr &&c) = 0; + virtual void onTimeout(Interest::Ptr &&i) = 0; + }; + + class ProducerCallback { + public: + virtual void onInterest(Interest::Ptr &&i) = 0; + }; + + Portal() : Portal(internal_io_service_) { + internal_work_ = std::make_unique(io_service_); + } + + Portal(asio::io_service &io_service) + : io_service_(io_service), + is_running_(false), + app_name_("libtransport_application"), + consumer_callback_(nullptr), + producer_callback_(nullptr), + connector_(std::bind(&Portal::processIncomingMessages, this, + std::placeholders::_1), + std::bind(&Portal::setLocalRoutes, this), io_service_, + app_name_), + forwarder_interface_(connector_) {} + + void setConsumerCallback(ConsumerCallback *consumer_callback) { + consumer_callback_ = consumer_callback; + } + + void setProducerCallback(ProducerCallback *producer_callback) { + producer_callback_ = producer_callback; + } + + TRANSPORT_ALWAYS_INLINE void setOutputInterface( + const std::string &output_interface) { + forwarder_interface_.setOutputInterface(output_interface); + } + + TRANSPORT_ALWAYS_INLINE void connect(bool is_consumer = true) { + forwarder_interface_.connect(is_consumer); + } + + ~Portal() { + connector_.close(); + stopEventsLoop(); + } + + TRANSPORT_ALWAYS_INLINE bool interestIsPending(const Name &name) { + auto it = pending_interest_hash_table_.find(name); + if (it != pending_interest_hash_table_.end()) + if (!it->second->isReceived()) return true; + + return false; + } + + TRANSPORT_ALWAYS_INLINE void sendInterest(Interest::Ptr &&interest) { + const Name name(interest->getName(), true); + + // Send it + forwarder_interface_.send(*interest); + + pending_interest_hash_table_[name] = std::make_unique( + std::move(interest), std::make_unique(io_service_)); + + pending_interest_hash_table_[name]->startCountdown( + std::bind(&Portal::timerHandler, this, + std::placeholders::_1, name)); + } + + TRANSPORT_ALWAYS_INLINE void timerHandler(const std::error_code &ec, + const Name &name) { + if (TRANSPORT_EXPECT_FALSE(!is_running_)) { + return; + } + + if (TRANSPORT_EXPECT_TRUE(!ec)) { + std::unordered_map>::iterator it = + pending_interest_hash_table_.find(name); + if (it != pending_interest_hash_table_.end()) { + std::unique_ptr ptr = std::move(it->second); + pending_interest_hash_table_.erase(it); + + if (consumer_callback_) { + consumer_callback_->onTimeout(std::move(ptr->getInterest())); + } + } + } + } + + TRANSPORT_ALWAYS_INLINE void bind(const BindConfig &config) { + connector_.enableBurst(); + forwarder_interface_.setContentStoreSize(config.csReserved()); + served_namespaces_.push_back(config.prefix()); + registerRoute(served_namespaces_.back()); + } + + TRANSPORT_ALWAYS_INLINE void runEventsLoop() { + if (io_service_.stopped()) { + io_service_.reset(); // ensure that run()/poll() will do some work + } + + is_running_ = true; + this->io_service_.run(); + is_running_ = false; + } + + TRANSPORT_ALWAYS_INLINE void runOneEvent() { + if (io_service_.stopped()) { + io_service_.reset(); // ensure that run()/poll() will do some work + } + + is_running_ = true; + this->io_service_.run_one(); + is_running_ = false; + } + + TRANSPORT_ALWAYS_INLINE void sendContentObject( + ContentObject &content_object) { + forwarder_interface_.send(content_object); + } + + TRANSPORT_ALWAYS_INLINE void stopEventsLoop() { + is_running_ = false; + internal_work_.reset(); + + for (auto &pend_interest : pending_interest_hash_table_) { + pend_interest.second->cancelTimer(); + } + + clear(); + + io_service_.post([this]() { io_service_.stop(); }); + } + + TRANSPORT_ALWAYS_INLINE void killConnection() { connector_.close(); } + + TRANSPORT_ALWAYS_INLINE void clear() { pending_interest_hash_table_.clear();} + + TRANSPORT_ALWAYS_INLINE asio::io_service &getIoService() { + return io_service_; + } + + TRANSPORT_ALWAYS_INLINE std::size_t getPITSize() { + connector_.state(); + return pending_interest_hash_table_.size(); + } + + TRANSPORT_ALWAYS_INLINE void registerRoute(Prefix &prefix) { + forwarder_interface_.registerRoute(prefix); + } + + private: + TRANSPORT_ALWAYS_INLINE void processIncomingMessages( + Packet::MemBufPtr &&packet_buffer) { + if (TRANSPORT_EXPECT_FALSE(!is_running_)) { + return; + } + + if (packet_buffer->data()[0] == ForwarderInt::ack_code) { + // Hicn forwarder message + processControlMessage(std::move(packet_buffer)); + return; + } + + bool is_interest = Packet::isInterest(packet_buffer->data()); + Packet::Format format = Packet::getFormatFromBuffer(packet_buffer->data()); + + if (TRANSPORT_EXPECT_TRUE(_is_tcp(format))) { + if (!is_interest) { + processContentObject( + ContentObject::Ptr(new ContentObject(std::move(packet_buffer)))); + } else { + processInterest(Interest::Ptr(new Interest(std::move(packet_buffer)))); + } + } else { + TRANSPORT_LOGE("Received not supported packet. Ignoring it."); + } + } + + TRANSPORT_ALWAYS_INLINE void setLocalRoutes() { + for (auto &name : served_namespaces_) { + registerRoute(name); + } + } + + TRANSPORT_ALWAYS_INLINE void processInterest(Interest::Ptr &&interest) { + // Interest for a producer + if (TRANSPORT_EXPECT_TRUE(producer_callback_ != nullptr)) { + producer_callback_->onInterest(std::move(interest)); + } + } + + TRANSPORT_ALWAYS_INLINE void processContentObject( + ContentObject::Ptr &&content_object) { + PendingInterestHashTable::iterator it = + pending_interest_hash_table_.find(content_object->getName()); + + if (TRANSPORT_EXPECT_TRUE(it != pending_interest_hash_table_.end())) { + std::unique_ptr interest_ptr = std::move(it->second); + interest_ptr->cancelTimer(); + + if (TRANSPORT_EXPECT_TRUE(!interest_ptr->isReceived())) { + interest_ptr->setReceived(); + pending_interest_hash_table_.erase(content_object->getName()); + + if (consumer_callback_) { + consumer_callback_->onContentObject( + std::move(interest_ptr->getInterest()), + std::move(content_object)); + } + + } else { + TRANSPORT_LOGW( + "Content already received (interest already satisfied)."); + } + } else { + TRANSPORT_LOGW("No pending interests for current content (%s)", + content_object->getName().toString().c_str()); + } + } + + TRANSPORT_ALWAYS_INLINE void processControlMessage( + Packet::MemBufPtr &&packet_buffer) { + // Control message as response to the route set by a producer. + // Do nothing + } + + private: + asio::io_service &io_service_; + asio::io_service internal_io_service_; + std::unique_ptr internal_work_; + + volatile bool is_running_; + + std::string app_name_; + + PendingInterestHashTable pending_interest_hash_table_; + + ConsumerCallback *consumer_callback_; + ProducerCallback *producer_callback_; + + typename ForwarderInt::ConnectorType connector_; + ForwarderInt forwarder_interface_; + + std::list served_namespaces_; + + ip_address_t locator4_; + ip_address_t locator6_; +}; + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/prefix.cc b/libtransport/src/hicn/transport/core/prefix.cc new file mode 100755 index 000000000..69c2b845a --- /dev/null +++ b/libtransport/src/hicn/transport/core/prefix.cc @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +extern "C" { +#include +} + +#include +#include +#include + +namespace transport { + +namespace core { + +Prefix::Prefix() { std::memset(&ip_address_, 0, sizeof(ip_address_t)); } + +Prefix::Prefix(const char *prefix) : Prefix(std::string(prefix)) {} + +Prefix::Prefix(std::string &&prefix) : Prefix(prefix) {} + +Prefix::Prefix(const std::string &prefix) { + utils::StringTokenizer st(prefix, "/"); + + std::string ip_address = st.nextToken(); + int family = get_addr_family(ip_address.c_str()); + + std::string prefix_length = family == AF_INET6 ? "128" : "32"; + + if (st.hasMoreTokens()) { + prefix_length = st.nextToken(); + } + + buildPrefix(ip_address, uint16_t(atoi(prefix_length.c_str())), family); +} + +Prefix::Prefix(std::string &prefix, uint16_t prefix_length) { + int family = get_addr_family(prefix.c_str()); + buildPrefix(prefix, prefix_length, family); +} + +Prefix::Prefix(const core::Name &content_name, uint16_t prefix_length) { + int family = content_name.getAddressFamily(); + + if (!checkPrefixLengthAndAddressFamily(prefix_length, family)) { + throw errors::InvalidIpAddressException(); + } + + ip_address_ = content_name.toIpAddress(); + ip_address_.prefix_len = prefix_length; + ip_address_.family = family; +} + +void Prefix::buildPrefix(std::string &prefix, uint16_t prefix_length, + int family) { + if (!checkPrefixLengthAndAddressFamily(prefix_length, family)) { + throw errors::InvalidIpAddressException(); + } + + int ret = inet_pton(family, prefix.c_str(), ip_address_.buffer); + + if (ret != 1) { + throw errors::InvalidIpAddressException(); + } + + ip_address_.prefix_len = prefix_length; + ip_address_.family = family; +} + +std::unique_ptr Prefix::toSockaddr() { + Sockaddr *ret = nullptr; + + switch (ip_address_.family) { + case AF_INET6: + ret = (Sockaddr *)new Sockaddr6; + break; + case AF_INET: + ret = (Sockaddr *)new Sockaddr4; + break; + default: + throw errors::InvalidIpAddressException(); + } + + if (hicn_ip_to_sockaddr_address(&ip_address_, ret) < 0) { + throw errors::InvalidIpAddressException(); + } + + return std::unique_ptr(ret); +} + +uint16_t Prefix::getPrefixLength() { return ip_address_.prefix_len; } + +Prefix &Prefix::setPrefixLength(uint16_t prefix_length) { + ip_address_.prefix_len = prefix_length; + return *this; +} + +int Prefix::getAddressFamily() { return ip_address_.family; } + +Prefix &Prefix::setAddressFamily(int address_family) { + ip_address_.family = address_family; + return *this; +} + +std::string Prefix::getNetwork() const { + if (!checkPrefixLengthAndAddressFamily(ip_address_.prefix_len, + ip_address_.family)) { + throw errors::InvalidIpAddressException(); + } + + std::size_t size = + ip_address_.family == AF_INET ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN; + + std::string network(size, 0); + + if (hicn_ip_ntop(&ip_address_, (char *)network.c_str(), size) < 0) { + throw errors::RuntimeException( + "Impossible to retrieve network from ip address."); + } + + return network; +} + +Name Prefix::getName() const { + std::string s(getNetwork()); + return Name(s); +} + +Prefix &Prefix::setNetwork(std::string &network) { + if (!inet_pton(AF_INET6, network.c_str(), ip_address_.buffer)) { + throw errors::RuntimeException("The network name is not valid."); + } + + return *this; +} + +Name Prefix::makeRandomName() const { + srand(time(nullptr)); + + if (ip_address_.family == AF_INET6) { + std::default_random_engine eng((std::random_device())()); + std::uniform_int_distribution idis( + 0, std::numeric_limits::max()); + uint64_t random_number = idis(eng); + + uint32_t hash_size_bits = IPV6_ADDR_LEN_BITS - ip_address_.prefix_len; + uint64_t ip_address[2]; + memcpy(ip_address, ip_address_.buffer, sizeof(uint64_t)); + memcpy(ip_address + 1, ip_address_.buffer + 8, sizeof(uint64_t)); + std::string network(IPV6_ADDR_LEN * 3, 0); + + // Let's do the magic ;) + int shift_size = hash_size_bits > sizeof(random_number) * 8 + ? sizeof(random_number) * 8 + : hash_size_bits; + + ip_address[1] >>= shift_size; + ip_address[1] <<= shift_size; + + ip_address[1] |= random_number >> (sizeof(uint64_t) * 8 - shift_size); + + if (!inet_ntop(ip_address_.family, ip_address, (char *)network.c_str(), + IPV6_ADDR_LEN * 3)) { + throw errors::RuntimeException( + "Impossible to retrieve network from ip address."); + } + + return Name(network); + } + + return Name(); +} + +bool Prefix::checkPrefixLengthAndAddressFamily(uint16_t prefix_length, + int family) { + // First check the family + if (family != AF_INET6 && family != AF_INET) { + return false; + } + + int max_addr_len_bits = + family == AF_INET6 ? IPV6_ADDR_LEN_BITS : IPV4_ADDR_LEN_BITS; + + if (prefix_length > max_addr_len_bits) { + return false; + } + + return true; +} + +ip_address_t &Prefix::toIpAddressStruct() { return ip_address_; } + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/hicn/transport/core/prefix.h b/libtransport/src/hicn/transport/core/prefix.h new file mode 100755 index 000000000..b68c6bdf6 --- /dev/null +++ b/libtransport/src/hicn/transport/core/prefix.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +namespace transport { + +namespace core { + +class Prefix { + public: + Prefix(); + + Prefix(const char *prefix); + + Prefix(const std::string &prefix); + + Prefix(std::string &&prefix); + + Prefix(std::string &prefix, uint16_t prefix_length); + + Prefix(const core::Name &content_name, uint16_t prefix_length); + + std::unique_ptr toSockaddr(); + + uint16_t getPrefixLength(); + + Prefix &setPrefixLength(uint16_t prefix_length); + + std::string getNetwork() const; + + Name getName() const; + + Prefix &setNetwork(std::string &network); + + int getAddressFamily(); + + Prefix &setAddressFamily(int address_family); + + Name makeRandomName() const; + + ip_address_t &toIpAddressStruct(); + + private: + static bool checkPrefixLengthAndAddressFamily(uint16_t prefix_length, + int family); + + void buildPrefix(std::string &prefix, uint16_t prefix_length, int family); + + ip_address_t ip_address_; +}; + +} // end namespace core + +} // end namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/raw_socket_connector.cc b/libtransport/src/hicn/transport/core/raw_socket_connector.cc new file mode 100755 index 000000000..5cfff39fb --- /dev/null +++ b/libtransport/src/hicn/transport/core/raw_socket_connector.cc @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define MY_DEST_MAC0 0x0a +#define MY_DEST_MAC1 0x7b +#define MY_DEST_MAC2 0x7c +#define MY_DEST_MAC3 0x1c +#define MY_DEST_MAC4 0x4a +#define MY_DEST_MAC5 0x14 + +namespace transport { + +namespace core { + +RawSocketConnector::RawSocketConnector( + PacketReceivedCallback &&receive_callback, + OnReconnect &&on_reconnect_callback, asio::io_service &io_service, + std::string app_name) + : Connector(), + io_service_(io_service), + socket_(io_service_, raw_protocol(PF_PACKET, SOCK_RAW)), + // resolver_(io_service_), + timer_(io_service_), + read_msg_(packet_pool_.makePtr(nullptr)), + data_available_(false), + receive_callback_(receive_callback), + on_reconnect_callback_(on_reconnect_callback), + app_name_(app_name) { + memset(&link_layer_address_, 0, sizeof(link_layer_address_)); +} + +RawSocketConnector::~RawSocketConnector() {} + +void RawSocketConnector::connect(const std::string &interface_name, + const std::string &mac_address_str) { + memset(ðernet_header_, 0, sizeof(ethernet_header_)); + struct ifreq ifr; + struct ifreq if_mac; + uint8_t mac_address[6]; + + utils::convertStringToMacAddress(mac_address_str, mac_address); + + // Get interface mac address + int fd = static_cast(socket_.native_handle()); + + /* Get the index of the interface to send on */ + memset(&ifr, 0, sizeof(struct ifreq)); + strncpy(ifr.ifr_name, interface_name.c_str(), interface_name.size()); + + // if (ioctl(fd, SIOCGIFINDEX, &if_idx) < 0) { + // perror("SIOCGIFINDEX"); + // } + + /* Get the MAC address of the interface to send on */ + memset(&if_mac, 0, sizeof(struct ifreq)); + strncpy(if_mac.ifr_name, interface_name.c_str(), interface_name.size()); + if (ioctl(fd, SIOCGIFHWADDR, &if_mac) < 0) { + perror("SIOCGIFHWADDR"); + throw errors::RuntimeException("Interface does not exist"); + } + + /* Ethernet header */ + for (int i = 0; i < 6; i++) { + ethernet_header_.ether_shost[i] = + ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[i]; + ethernet_header_.ether_dhost[i] = mac_address[i]; + } + + /* Ethertype field */ + ethernet_header_.ether_type = htons(ETH_P_IPV6); + + strcpy(ifr.ifr_name, interface_name.c_str()); + + if (0 == ioctl(fd, SIOCGIFHWADDR, &ifr)) { + memcpy(link_layer_address_.sll_addr, ifr.ifr_hwaddr.sa_data, 6); + } + + // memset(&ifr, 0, sizeof(ifr)); + // ioctl(fd, SIOCGIFFLAGS, &ifr); + // ifr.ifr_flags |= IFF_PROMISC; + // ioctl(fd, SIOCSIFFLAGS, &ifr); + + link_layer_address_.sll_family = AF_PACKET; + link_layer_address_.sll_protocol = htons(ETH_P_ALL); + link_layer_address_.sll_ifindex = if_nametoindex(interface_name.c_str()); + link_layer_address_.sll_hatype = 1; + link_layer_address_.sll_halen = 6; + + // startConnectionTimer(); + doConnect(); + doRecvPacket(); +} + +void RawSocketConnector::state() { return; } + +void RawSocketConnector::send(const uint8_t *packet, std::size_t len, + const PacketSentCallback &packet_sent) { + // asio::async_write(socket_, asio::buffer(packet, len), + // [packet_sent] (std::error_code ec, + // std::size_t /*length*/) { + // packet_sent(); + // }); +} + +void RawSocketConnector::send(const Packet::MemBufPtr &packet) { + // Packet &p = const_cast(packet); + // p.setTcpChecksum(); + + // if (!p.checkIntegrity()) { + // TRANSPORT_LOGW("Sending message with wrong checksum!!!"); + // } + + // std::shared_ptr ptr; + // try { + // ptr = packet.shared_from_this(); + // } catch (std::bad_weak_ptr& exc) { + // TRANSPORT_LOGW("Sending interest which has not been created using a + // shared PTR! A copy will be made."); ptr = + // std::shared_ptr(packet.clone()); + // } + + io_service_.post([this, packet]() { + bool write_in_progress = !output_buffer_.empty(); + output_buffer_.push_back(std::move(packet)); + if (!write_in_progress) { + doSendPacket(); + } else { + // Tell the handle connect it has data to write + data_available_ = true; + } + }); +} + +void RawSocketConnector::close() { + io_service_.post([this]() { socket_.close(); }); +} + +void RawSocketConnector::doSendPacket() { + auto packet = output_buffer_.front().get(); + auto array = std::vector(); + + const utils::MemBuf *current = packet; + do { + array.push_back(asio::const_buffer(current->data(), current->length())); + current = current->next(); + } while (current != packet); + + socket_.async_send( + std::move(array), + [this /*, packet*/](std::error_code ec, std::size_t bytes_transferred) { + if (TRANSPORT_EXPECT_TRUE(!ec)) { + output_buffer_.pop_front(); + if (!output_buffer_.empty()) { + doSendPacket(); + } + } else { + TRANSPORT_LOGE("%d %s", ec.value(), ec.message().c_str()); + } + }); +} + +void RawSocketConnector::doRecvPacket() { + read_msg_ = std::move(getPacket()); + socket_.async_receive( + asio::buffer(read_msg_->writableData(), packet_size), + [this](std::error_code ec, std::size_t bytes_transferred) mutable { + if (!ec) { + // Ignore packets that are not for us + uint8_t *dst_mac_address = const_cast(read_msg_->data()); + if (!std::memcmp(dst_mac_address, ethernet_header_.ether_shost, + ETHER_ADDR_LEN)) { + read_msg_->append(bytes_transferred); + read_msg_->trimStart(sizeof(struct ether_header)); + receive_callback_(std::move(read_msg_)); + } + } else { + TRANSPORT_LOGE("%d %s", ec.value(), ec.message().c_str()); + } + doRecvPacket(); + }); +} + +void RawSocketConnector::doConnect() { + socket_.bind(raw_endpoint(&link_layer_address_, sizeof(link_layer_address_))); +} + +void RawSocketConnector::enableBurst() { return; } + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/raw_socket_connector.h b/libtransport/src/hicn/transport/core/raw_socket_connector.h new file mode 100755 index 000000000..5e39efa0e --- /dev/null +++ b/libtransport/src/hicn/transport/core/raw_socket_connector.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +namespace transport { + +namespace core { + +using asio::generic::raw_protocol; +using raw_endpoint = asio::generic::basic_endpoint; + +class RawSocketConnector : public Connector { + public: + RawSocketConnector(PacketReceivedCallback &&receive_callback, + OnReconnect &&reconnect_callback, + asio::io_service &io_service, + std::string app_name = "Libtransport"); + + ~RawSocketConnector() override; + + void send(const Packet::MemBufPtr &packet) override; + + void send(const uint8_t *packet, std::size_t len, + const PacketSentCallback &packet_sent = 0) override; + + void close() override; + + void enableBurst() override; + + void connect(const std::string &interface_name, + const std::string &mac_address_str); + + void state() override; + + private: + void doConnect(); + + void doRecvPacket(); + + void doSendPacket(); + + private: + asio::io_service &io_service_; + raw_protocol::socket socket_; + + struct ether_header ethernet_header_; + + struct sockaddr_ll link_layer_address_; + + asio::steady_timer timer_; + + utils::ObjectPool::Ptr read_msg_; + + bool data_available_; + + PacketReceivedCallback receive_callback_; + OnReconnect on_reconnect_callback_; + std::string app_name_; +}; + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/raw_socket_interface.cc b/libtransport/src/hicn/transport/core/raw_socket_interface.cc new file mode 100755 index 000000000..37aaff7e0 --- /dev/null +++ b/libtransport/src/hicn/transport/core/raw_socket_interface.cc @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +namespace transport { + +namespace core { + +static std::string config_folder_path = "/etc/transport/interface.conf.d"; + +RawSocketInterface::RawSocketInterface(RawSocketConnector &connector) + : ForwarderInterface(connector) {} + +RawSocketInterface::~RawSocketInterface() {} + +void RawSocketInterface::connect(bool is_consumer) { + std::string complete_filename = + config_folder_path + std::string("/") + output_interface_; + + std::ifstream is(complete_filename); + std::string interface; + + if (is) { + is >> remote_mac_address_; + } + + // Get interface ip address + struct sockaddr_in6 address; + utils::retrieveInterfaceAddress(output_interface_, &address); + inet6_address_.family = address.sin6_family; + + std::memcpy(inet6_address_.buffer, &address.sin6_addr, + sizeof(address.sin6_addr)); + connector_.connect(output_interface_, remote_mac_address_); +} + +void RawSocketInterface::registerRoute(Prefix &prefix) { return; } + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/hicn/transport/core/raw_socket_interface.h b/libtransport/src/hicn/transport/core/raw_socket_interface.h new file mode 100755 index 000000000..c030af662 --- /dev/null +++ b/libtransport/src/hicn/transport/core/raw_socket_interface.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include +#include + +namespace transport { + +namespace core { + +class RawSocketInterface + : public ForwarderInterface { + public: + typedef RawSocketConnector ConnectorType; + + RawSocketInterface(RawSocketConnector &connector); + + ~RawSocketInterface(); + + void connect(bool is_consumer); + + void registerRoute(Prefix &prefix); + + std::uint16_t getMtu() { return interface_mtu; } + + private: + static constexpr std::uint16_t interface_mtu = 1500; + std::string remote_mac_address_; +}; + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/hicn/transport/core/socket_connector.cc b/libtransport/src/hicn/transport/core/socket_connector.cc new file mode 100755 index 000000000..332b87ec7 --- /dev/null +++ b/libtransport/src/hicn/transport/core/socket_connector.cc @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include + +namespace transport { + +namespace core { + +namespace { +class NetworkMessage { + public: + static constexpr std::size_t fixed_header_length = 10; + + static std::size_t decodeHeader(const uint8_t *packet) { + // General checks + // CCNX Control packet format + uint8_t first_byte = packet[0]; + uint8_t ip_format = (packet[0] & 0xf0) >> 4; + + if (TRANSPORT_EXPECT_FALSE(first_byte == 102)) { + // Get packet length + return 44; + } else if (TRANSPORT_EXPECT_TRUE(ip_format == 6 || ip_format == 4)) { + Packet::Format format = Packet::getFormatFromBuffer(packet); + return Packet::getHeaderSizeFromBuffer(format, packet) + + Packet::getPayloadSizeFromBuffer(format, packet); + } + + return 0; + } +}; +} // namespace + +SocketConnector::SocketConnector(PacketReceivedCallback &&receive_callback, + OnReconnect &&on_reconnect_callback, + asio::io_service &io_service, + std::string app_name) + : Connector(), + io_service_(io_service), + socket_(io_service_), + resolver_(io_service_), + timer_(io_service_), + read_msg_(packet_pool_.makePtr(nullptr)), + is_connecting_(false), + is_reconnection_(false), + data_available_(false), + receive_callback_(receive_callback), + on_reconnect_callback_(on_reconnect_callback), + app_name_(app_name) {} + +SocketConnector::~SocketConnector() {} + +void SocketConnector::connect(std::string ip_address, std::string port) { + endpoint_iterator_ = resolver_.resolve( + {ip_address, port, asio::ip::resolver_query_base::numeric_service}); + + startConnectionTimer(); + doConnect(); +} + +void SocketConnector::state() { return; } + +void SocketConnector::send(const uint8_t *packet, std::size_t len, + const PacketSentCallback &packet_sent) { + asio::async_write(socket_, asio::buffer(packet, len), + [packet_sent](std::error_code ec, std::size_t /*length*/) { + packet_sent(); + }); +} + +void SocketConnector::send(const Packet::MemBufPtr &packet) { + io_service_.post([this, packet]() { + bool write_in_progress = !output_buffer_.empty(); + output_buffer_.push_back(std::move(packet)); + if (TRANSPORT_EXPECT_FALSE(!is_connecting_)) { + if (!write_in_progress) { + doWrite(); + } + } else { + // Tell the handle connect it has data to write + data_available_ = true; + } + }); +} + +void SocketConnector::close() { + io_service_.post([this]() { socket_.close(); }); +} + +void SocketConnector::doWrite() { + // TODO improve this piece of code for sending many buffers togethers + // if list contains more than one packet + auto packet = output_buffer_.front().get(); + auto array = std::vector(); + + const utils::MemBuf *current = packet; + do { + array.push_back(asio::const_buffer(current->data(), current->length())); + current = current->next(); + } while (current != packet); + + asio::async_write( + socket_, std::move(array), + [this /*, packet*/](std::error_code ec, std::size_t length) { + if (TRANSPORT_EXPECT_TRUE(!ec)) { + output_buffer_.pop_front(); + if (!output_buffer_.empty()) { + doWrite(); + } + } else { + TRANSPORT_LOGE("%d %s", ec.value(), ec.message().c_str()); + tryReconnect(); + } + }); +} + +void SocketConnector::doReadBody(std::size_t body_length) { + asio::async_read( + socket_, asio::buffer(read_msg_->writableTail(), body_length), + asio::transfer_exactly(body_length), + [this](std::error_code ec, std::size_t length) { + read_msg_->append(length); + if (TRANSPORT_EXPECT_TRUE(!ec)) { + receive_callback_(std::move(read_msg_)); + doReadHeader(); + } else { + TRANSPORT_LOGE("%d %s", ec.value(), ec.message().c_str()); + tryReconnect(); + } + }); +} + +void SocketConnector::doReadHeader() { + read_msg_ = getPacket(); + asio::async_read( + socket_, + asio::buffer(read_msg_->writableData(), + NetworkMessage::fixed_header_length), + asio::transfer_exactly(NetworkMessage::fixed_header_length), + [this](std::error_code ec, std::size_t length) { + if (TRANSPORT_EXPECT_TRUE(!ec)) { + read_msg_->append(NetworkMessage::fixed_header_length); + std::size_t body_length = 0; + if ((body_length = NetworkMessage::decodeHeader(read_msg_->data())) > + 0) { + doReadBody(body_length - length); + } else { + TRANSPORT_LOGE("Decoding error. Ignoring packet."); + } + } else { + TRANSPORT_LOGE("%d %s", ec.value(), ec.message().c_str()); + tryReconnect(); + } + }); +} + +void SocketConnector::tryReconnect() { + if (!is_connecting_) { + TRANSPORT_LOGE("Connection lost. Trying to reconnect...\n"); + is_connecting_ = true; + is_reconnection_ = true; + io_service_.post([this]() { + socket_.close(); + startConnectionTimer(); + doConnect(); + }); + } +} + +void SocketConnector::doConnect() { + asio::async_connect(socket_, endpoint_iterator_, + [this](std::error_code ec, tcp::resolver::iterator) { + if (!ec) { + timer_.cancel(); + is_connecting_ = false; + asio::ip::tcp::no_delay noDelayOption(true); + socket_.set_option(noDelayOption); + doReadHeader(); + + if (data_available_) { + data_available_ = false; + doWrite(); + } + + if (is_reconnection_) { + is_reconnection_ = false; + TRANSPORT_LOGI("Connection recovered!\n"); + on_reconnect_callback_(); + } + } else { + sleep(1); + doConnect(); + } + }); +} + +bool SocketConnector::checkConnected() { return !is_connecting_; } + +void SocketConnector::enableBurst() { return; } + +void SocketConnector::startConnectionTimer() { + timer_.expires_from_now(std::chrono::seconds(60)); + timer_.async_wait( + std::bind(&SocketConnector::handleDeadline, this, std::placeholders::_1)); +} + +void SocketConnector::handleDeadline(const std::error_code &ec) { + if (!ec) { + io_service_.post([this]() { + socket_.close(); + TRANSPORT_LOGE("Error connecting. Is the forwarder running?\n"); + io_service_.stop(); + }); + } +} + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/socket_connector.h b/libtransport/src/hicn/transport/core/socket_connector.h new file mode 100755 index 000000000..d7a05aab4 --- /dev/null +++ b/libtransport/src/hicn/transport/core/socket_connector.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include +#include + +namespace transport { +namespace core { + +using asio::ip::tcp; + +class SocketConnector : public Connector { + public: + SocketConnector(PacketReceivedCallback &&receive_callback, + OnReconnect &&reconnect_callback, + asio::io_service &io_service, + std::string app_name = "Libtransport"); + + ~SocketConnector() override; + + void send(const Packet::MemBufPtr &packet) override; + + void send(const uint8_t *packet, std::size_t len, + const PacketSentCallback &packet_sent = 0) override; + + void close() override; + + void enableBurst() override; + + void connect(std::string ip_address = "127.0.0.1", std::string port = "9695"); + + void state() override; + + private: + void doConnect(); + + void doReadHeader(); + + void doReadBody(std::size_t body_length); + + void doWrite(); + + bool checkConnected(); + + private: + void handleDeadline(const std::error_code &ec); + + void startConnectionTimer(); + + void tryReconnect(); + + asio::io_service &io_service_; + asio::ip::tcp::socket socket_; + asio::ip::tcp::resolver resolver_; + asio::ip::tcp::resolver::iterator endpoint_iterator_; + asio::steady_timer timer_; + + utils::ObjectPool::Ptr read_msg_; + + bool is_connecting_; + bool is_reconnection_; + bool data_available_; + + PacketReceivedCallback receive_callback_; + OnReconnect on_reconnect_callback_; + std::string app_name_; +}; + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/test/CMakeLists.txt b/libtransport/src/hicn/transport/core/test/CMakeLists.txt new file mode 100755 index 000000000..48c50e9b0 --- /dev/null +++ b/libtransport/src/hicn/transport/core/test/CMakeLists.txt @@ -0,0 +1,10 @@ +# Enable gcov output for the tests +add_definitions(--coverage) +set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage") + +set(TestsExpectedToPass + test_core_manifest) + +foreach(test ${TestsExpectedToPass}) + AddTest(${test}) +endforeach() \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/test/test_core_manifest.cc b/libtransport/src/hicn/transport/core/test/test_core_manifest.cc new file mode 100755 index 000000000..58563d8f9 --- /dev/null +++ b/libtransport/src/hicn/transport/core/test/test_core_manifest.cc @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "../manifest_format_fixed.h" +#include "../manifest_inline.h" + +#include +#include +#include + +namespace transport { + +namespace core { + +namespace { +// The fixture for testing class Foo. +class ManifestTest : public ::testing::Test { + protected: + using ContentObjectManifest = ManifestInline; + + ManifestTest() : name_("b001::123|321"), manifest1_(name_) { + // You can do set-up work for each test here. + } + + virtual ~ManifestTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() { + // Code here will be called immediately after the constructor (right + // before each test). + } + + virtual void TearDown() { + // Code here will be called immediately after each test (right + // before the destructor). + } + + Name name_; + ContentObjectManifest manifest1_; + + std::vector manifest_payload = { + 0x11, 0x11, 0x01, 0x00, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad // , 0x00, 0x00, + // 0x00, 0x45, 0xa3, + // 0xd1, 0xf2, 0x2b, + // 0x94, 0x41, 0x22, + // 0xc9, 0x00, 0x00, + // 0x00, 0x44, 0xa3, + // 0xd1, 0xf2, 0x2b, + // 0x94, 0x41, 0x22, + // 0xc8 + }; +}; + +} // namespace + +TEST_F(ManifestTest, ManifestCreate) { + ContentObjectManifest manifest2(name_); + ContentObjectManifest manifest3 = manifest2; + + EXPECT_EQ(manifest1_, manifest2); + EXPECT_EQ(manifest1_, manifest3); +} + +TEST_F(ManifestTest, ManifestCreateFromBase) { + ContentObject content_object(name_); + content_object.setPayload(manifest_payload.data(), manifest_payload.size()); + ContentObjectManifest manifest(std::move(content_object)); + + auto manifest4 = ContentObjectManifest::createManifest( + name_, core::ManifestVersion::VERSION_1, + core::ManifestType::INLINE_MANIFEST, HashAlgorithm::SHA_256, true, + core::Name("b001::dead"), + core::NextSegmentCalculationStrategy::INCREMENTAL, 128); + + manifest4->encode(); + manifest4->dump(); + manifest.dump(); + + EXPECT_EQ(manifest1_, manifest); + // EXPECT_EQ(manifest1_, manifest3); +} + +TEST_F(ManifestTest, SetLastManifest) { + manifest1_.clear(); + + manifest1_.setFinalManifest(true); + manifest1_.encode(); + manifest1_.decode(); + bool fcn = manifest1_.isFinalManifest(); + + ASSERT_TRUE(fcn); +} + +TEST_F(ManifestTest, SetManifestType) { + manifest1_.clear(); + + ManifestType type1 = ManifestType::INLINE_MANIFEST; + ManifestType type2 = ManifestType::FLIC_MANIFEST; + + manifest1_.setManifestType(type1); + manifest1_.encode(); + manifest1_.decode(); + ManifestType type_returned1 = manifest1_.getManifestType(); + + manifest1_.clear(); + + manifest1_.setManifestType(type2); + manifest1_.encode(); + manifest1_.decode(); + ManifestType type_returned2 = manifest1_.getManifestType(); + + ASSERT_EQ(type1, type_returned1); + ASSERT_EQ(type2, type_returned2); +} + +TEST_F(ManifestTest, SetHashAlgorithm) { + manifest1_.clear(); + + HashAlgorithm hash1 = HashAlgorithm::SHA_512; + HashAlgorithm hash2 = HashAlgorithm::CRC32C; + HashAlgorithm hash3 = HashAlgorithm::SHA_256; + + manifest1_.setHashAlgorithm(hash1); + manifest1_.encode(); + manifest1_.decode(); + HashAlgorithm type_returned1 = manifest1_.getHashAlgorithm(); + + manifest1_.clear(); + + manifest1_.setHashAlgorithm(hash2); + manifest1_.encode(); + manifest1_.decode(); + HashAlgorithm type_returned2 = manifest1_.getHashAlgorithm(); + + manifest1_.clear(); + + manifest1_.setHashAlgorithm(hash3); + manifest1_.encode(); + manifest1_.decode(); + HashAlgorithm type_returned3 = manifest1_.getHashAlgorithm(); + + ASSERT_EQ(hash1, type_returned1); + ASSERT_EQ(hash2, type_returned2); + ASSERT_EQ(hash3, type_returned3); +} + +TEST_F(ManifestTest, SetNextSegmentCalculationStrategy) { + manifest1_.clear(); + + NextSegmentCalculationStrategy strategy1 = + NextSegmentCalculationStrategy::INCREMENTAL; + + manifest1_.setNextSegmentCalculationStrategy(strategy1); + manifest1_.encode(); + manifest1_.decode(); + NextSegmentCalculationStrategy type_returned1 = + manifest1_.getNextSegmentCalculationStrategy(); + + ASSERT_EQ(strategy1, type_returned1); +} + +TEST_F(ManifestTest, SetBaseName) { + manifest1_.clear(); + + core::Name base_name("b001::dead"); + manifest1_.setBaseName(base_name); + manifest1_.encode(); + manifest1_.decode(); + core::Name ret_name = manifest1_.getBaseName(); + + ASSERT_EQ(base_name, ret_name); +} + +TEST_F(ManifestTest, SetSuffixList) { + manifest1_.clear(); + + core::Name base_name("b001::dead"); + + using random_bytes_engine = + std::independent_bits_engine; + random_bytes_engine rbe; + + std::default_random_engine eng((std::random_device())()); + std::uniform_int_distribution idis( + 0, std::numeric_limits::max()); + + auto entries = new std::pair[3]; + uint32_t suffixes[3]; + std::vector data[3]; + + for (int i = 0; i < 3; i++) { + data[i].resize(32); + std::generate(std::begin(data[i]), std::end(data[i]), std::ref(rbe)); + suffixes[i] = idis(eng); + entries[i] = std::make_pair( + suffixes[i], utils::CryptoHash(data[i].data(), data[i].size(), + utils::CryptoHashType::SHA_256)); + manifest1_.addSuffixHash(entries[i].first, entries[i].second); + } + + manifest1_.setBaseName(base_name); + + manifest1_.encode(); + manifest1_.decode(); + + core::Name ret_name = manifest1_.getBaseName(); + + // auto & hash_list = manifest1_.getSuffixHashList(); + + bool cond; + int i = 0; + + // for (auto & item : manifest1_.getSuffixList()) { + // auto hash = manifest1_.getHash(suffixes[i]); + // cond = utils::CryptoHash::compareBinaryDigest(hash, + // entries[i].second.getDigest().data(), + // entries[i].second.getType()); + // ASSERT_TRUE(cond); + // i++; + // } + + ASSERT_EQ(base_name, ret_name); + + delete[] entries; +} + +TEST_F(ManifestTest, EstimateSize) { + manifest1_.clear(); + + HashAlgorithm hash1 = HashAlgorithm::SHA_256; + NextSegmentCalculationStrategy strategy1 = + NextSegmentCalculationStrategy::INCREMENTAL; + ManifestType type1 = ManifestType::INLINE_MANIFEST; + core::Name base_name1("b001:abcd:fede:baba:cece:d0d0:face:dead"); + + manifest1_.setFinalManifest(true); + manifest1_.setBaseName(base_name1); + manifest1_.setNextSegmentCalculationStrategy(strategy1); + manifest1_.setHashAlgorithm(hash1); + manifest1_.setManifestType(type1); + + std::default_random_engine eng((std::random_device())()); + std::uniform_int_distribution idis( + 0, std::numeric_limits::max()); + + using random_bytes_engine = + std::independent_bits_engine; + random_bytes_engine rbe; + + while (manifest1_.estimateManifestSize(1) < 1440) { + uint32_t suffix = static_cast(idis(eng)); + std::vector data(32); + std::generate(std::begin(data), std::end(data), std::ref(rbe)); + auto hash = utils::CryptoHash(data.data(), data.size(), + utils::CryptoHashType::SHA_256); + manifest1_.addSuffixHash(suffix, hash); + } + + manifest1_.encode(); + manifest1_.decode(); + + manifest1_.dump(); + + ASSERT_GT(manifest1_.estimateManifestSize(), 0); + ASSERT_LT(manifest1_.estimateManifestSize(), 1500); +} + +} // namespace core + +} // namespace transport + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/vpp_binary_api.c b/libtransport/src/hicn/transport/core/vpp_binary_api.c new file mode 100755 index 000000000..ab23d3cf5 --- /dev/null +++ b/libtransport/src/hicn/transport/core/vpp_binary_api.c @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#ifdef __vpp__ + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#define vl_typedefs +#include +#undef vl_typedefs + +#define vl_endianfun +#include +#undef vl_endianfun + +#define vl_print(handle, ...) +#define vl_printfun +#include +#undef vl_printfun + +/* Get CRC codes of the messages */ +#define vl_msg_name_crc_list +#include +#undef vl_msg_name_crc_list + +// #define vl_api_version(n,v) static u32 vpe_api_version = (v); +// #include +// #undef vl_api_version + +#define POINTER_MAP_SIZE 32 +static void *global_pointers_map[POINTER_MAP_SIZE]; +static uint8_t global_pointers_map_index = 0; + +/* + * Table of message reply handlers, must include boilerplate handlers + * we just generated + */ +#define foreach_memif_api_reply_msg \ + _(MEMIF_CREATE_REPLY, memif_create_reply) \ + _(MEMIF_DELETE_REPLY, memif_delete_reply) \ + _(MEMIF_DETAILS, memif_details) + +/** + * @brief Generic VPP request structure. + */ +typedef struct __attribute__((packed)) vl_generic_request_s { + u16 _vl_msg_id; + u32 client_index; + u32 context; +} vl_generic_request_t; + +/** + * @brief Generic VPP reply structure (response with a single message). + */ +typedef struct __attribute__((packed)) vl_generic_reply_s { + u16 _vl_msg_id; + u32 context; + i32 retval; +} vl_generic_reply_t; + +static void vl_api_control_ping_reply_t_handler( + vl_api_control_ping_reply_t *mp) { + // Just unblock main thread + vpp_binary_api_t *binary_api = global_pointers_map[mp->context]; + binary_api->ret_val = ntohl(mp->retval); + vpp_binary_api_unlock_waiting_thread(binary_api); +} + +static void vl_api_sw_interface_set_flags_reply_t_handler( + vl_api_control_ping_reply_t *mp) { + // Unblock main thread setting reply message status code + vpp_binary_api_t *binary_api = global_pointers_map[mp->context]; + binary_api->ret_val = ntohl(mp->retval); + vpp_binary_api_unlock_waiting_thread(binary_api); +} + +static int vpp_connect_to_vlib(vpp_binary_api_t *binary_api, char *name) { + clib_mem_init_thread_safe(0, 256 << 20); + if (vl_client_connect_to_vlib("/vpe-api", name, 32) < 0) { + return -1; + } + + binary_api->vl_input_queue = binary_api->api_main->shmem_hdr->vl_input_queue; + binary_api->my_client_index = binary_api->api_main->my_client_index; + + return 0; +} + +vpp_binary_api_t *vpp_binary_api_init(const char *app_name) { + vpp_binary_api_t *ret = malloc(sizeof(vpp_binary_api_t)); + ret->api_main = &api_main; + ret->vlib_main = &vlib_global_main; + + vpp_connect_to_vlib(ret, (char *)app_name); + ret->semaphore = sem_open(app_name, O_CREAT, 0, 0); + + return ret; +} + +void vpp_binary_api_destroy(vpp_binary_api_t *api) { + sem_close(api->semaphore); + free(api); + vl_client_disconnect_from_vlib(); +} + +void vpp_binary_api_unlock_waiting_thread(vpp_binary_api_t *api) { + sem_post(api->semaphore); +} + +void vpp_binary_api_send_receive_ping(vpp_binary_api_t *api) { + /* Use a control ping for synchronization */ + + /* Get the control ping ID */ +#define _(id, n, crc) \ + const char *id##_CRC __attribute__((unused)) = #n "_" #crc; + foreach_vl_msg_name_crc_vpe; +#undef _ + + int ping_reply_id = + vl_msg_api_get_msg_index((u8 *)(VL_API_CONTROL_PING_REPLY_CRC)); + vl_msg_api_set_handlers(ping_reply_id, "control_ping_reply", + vl_api_control_ping_reply_t_handler, vl_noop_handler, + vl_api_control_ping_reply_t_endian, + vl_api_control_ping_reply_t_print, + sizeof(vl_api_control_ping_reply_t), 1); + + vl_api_control_ping_t *mp_ping; + mp_ping = vl_msg_api_alloc_as_if_client(sizeof(*mp_ping)); + mp_ping->_vl_msg_id = clib_host_to_net_u16( + vl_msg_api_get_msg_index((u8 *)(VL_API_CONTROL_PING_CRC))); + mp_ping->client_index = api->my_client_index; + + global_pointers_map[global_pointers_map_index] = api; + mp_ping->context = global_pointers_map_index++; + global_pointers_map_index %= POINTER_MAP_SIZE; + + TRANSPORT_LOGI("Sending ping id %u", mp_ping->_vl_msg_id); + + vpp_binary_api_send_request_wait_reply(api, mp_ping); +} + +int vpp_binary_api_set_int_state(vpp_binary_api_t *api, uint32_t sw_index, + link_state_t state) { +#define _(id, n, crc) \ + const char *id##_CRC __attribute__((unused)) = #n "_" #crc; + foreach_vl_msg_name_crc_vpe; +#undef _ + + int sw_interface_set_flags_reply_id = VL_API_SW_INTERFACE_SET_FLAGS_REPLY; + vl_msg_api_set_handlers( + sw_interface_set_flags_reply_id, "sw_interface_set_flags_reply", + vl_api_sw_interface_set_flags_reply_t_handler, vl_noop_handler, + vl_api_sw_interface_set_flags_reply_t_endian, + vl_api_sw_interface_set_flags_reply_t_print, + sizeof(vl_api_sw_interface_set_flags_reply_t), 1); + + vl_api_sw_interface_set_flags_t *mp; + mp = vl_msg_api_alloc_as_if_client(sizeof(*mp)); + mp->_vl_msg_id = clib_host_to_net_u16(VL_API_SW_INTERFACE_SET_FLAGS); + mp->client_index = api->my_client_index; + mp->sw_if_index = clib_host_to_net_u32(sw_index); + mp->admin_up_down = (u8)state; + + global_pointers_map[global_pointers_map_index] = api; + mp->context = global_pointers_map_index++; + global_pointers_map_index %= POINTER_MAP_SIZE; + + TRANSPORT_LOGI("Sending set int flags id %u", mp->_vl_msg_id); + + return vpp_binary_api_send_request_wait_reply(api, mp); +} + +void vpp_binary_api_send_request(vpp_binary_api_t *api, void *request) { + vl_generic_request_t *req = NULL; + + req = (vl_generic_request_t *)request; + TRANSPORT_LOGI("Sending a request to VPP (id=%d).\n", ntohs(req->_vl_msg_id)); + + S(api, req); +} + +int vpp_binary_api_send_request_wait_reply(vpp_binary_api_t *api, + void *request) { + vpp_binary_api_send_request(api, request); + + sem_wait(api->semaphore); + + return api->ret_val; +} + +#endif // __vpp__ \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/vpp_binary_api.h b/libtransport/src/hicn/transport/core/vpp_binary_api.h new file mode 100755 index 000000000..1eb10e766 --- /dev/null +++ b/libtransport/src/hicn/transport/core/vpp_binary_api.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#ifdef __vpp__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef struct vpp_binary_api vpp_binary_api_t; +typedef struct vpp_plugin_binary_api vpp_plugin_binary_api_t; + +typedef enum link_state_s { UP = 1, DOWN = 0 } link_state_t; + +/** + * @brief Instantiate a new vpp_binary_api_t data structure and + * connect the application to the local VPP forwarder. + */ +vpp_binary_api_t* vpp_binary_api_init(const char* app_name); + +/** + * @brief Destroy the vpp_binary_api_t and disconnect from VPP. + */ +void vpp_binary_api_destroy(vpp_binary_api_t* api); + +void vpp_binary_api_send_receive_ping(vpp_binary_api_t* api); + +int vpp_binary_api_set_int_state(vpp_binary_api_t* api, uint32_t sw_index, + link_state_t state); + +/** + * @brief Send request to VPP and wait for reply. + */ +int vpp_binary_api_send_request_wait_reply(vpp_binary_api_t* api, + void* request); + +void vpp_binary_api_unlock_waiting_thread(vpp_binary_api_t* api); + +void vpp_binary_api_send_request(vpp_binary_api_t* api, void* request); + +#ifdef __cplusplus +} +#endif + +#endif // __vpp__ \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/vpp_binary_api_internal.h b/libtransport/src/hicn/transport/core/vpp_binary_api_internal.h new file mode 100755 index 000000000..22b665e96 --- /dev/null +++ b/libtransport/src/hicn/transport/core/vpp_binary_api_internal.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#ifdef __vpp__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +struct vpp_binary_api { + api_main_t *api_main; + u32 my_client_index; + unix_shared_memory_queue_t *vl_input_queue; + vlib_main_t *vlib_main; + sem_t *semaphore; + u32 ping_id; + int ret_val; + void *user_param; +}; + +struct vpp_plugin_binary_api { + vpp_binary_api_t *vpp_api; + u16 msg_id_base; + u32 my_client_index; +}; + +#define M(T, mp) \ + do { \ + mp = vl_msg_api_alloc_as_if_client(sizeof(*mp)); \ + memset(mp, 0, sizeof(*mp)); \ + mp->_vl_msg_id = ntohs(VL_API_##T + hm->msg_id_base); \ + mp->client_index = hm->my_client_index; \ + } while (0); + +#define S(api, mp) (vl_msg_api_send_shmem(api->vl_input_queue, (u8 *)&mp)) + +#ifdef __cplusplus +} +#endif + +#endif // __vpp__ \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/vpp_forwarder_interface.cc b/libtransport/src/hicn/transport/core/vpp_forwarder_interface.cc new file mode 100755 index 000000000..3a748c821 --- /dev/null +++ b/libtransport/src/hicn/transport/core/vpp_forwarder_interface.cc @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#ifdef __vpp__ + +#include +#include +#include + +typedef enum { MASTER = 0, SLAVE = 1 } memif_role_t; + +#define MEMIF_DEFAULT_RING_SIZE 2048 +#define MEMIF_DEFAULT_RX_QUEUES 1 +#define MEMIF_DEFAULT_TX_QUEUES 1 +#define MEMIF_DEFAULT_BUFFER_SIZE 2048 + +namespace transport { + +namespace core { + +vpp_binary_api_t *VPPForwarderInterface::api_ = nullptr; +vpp_plugin_binary_api_t *VPPForwarderInterface::memif_api_ = nullptr; +vpp_plugin_binary_api_t *VPPForwarderInterface::hicn_api_ = nullptr; +std::mutex VPPForwarderInterface::global_lock_; + +VPPForwarderInterface::VPPForwarderInterface(MemifConnector &connector) + : ForwarderInterface(connector), + sw_if_index_(~0), + face_id_(~0) {} + +VPPForwarderInterface::~VPPForwarderInterface() {} + +/** + * @brief Create a memif interface in the local VPP forwarder. + */ +uint32_t VPPForwarderInterface::getMemifConfiguration() { + memif_create_params_t input_params = {0}; + + memif_id_ = + memif_binary_api_get_next_memif_id(VPPForwarderInterface::memif_api_); + + input_params.id = memif_id_; + input_params.role = memif_role_t::MASTER; + input_params.mode = memif_interface_mode_t::MEMIF_INTERFACE_MODE_IP; + input_params.rx_queues = MEMIF_DEFAULT_RX_QUEUES; + input_params.tx_queues = MEMIF_DEFAULT_TX_QUEUES; + input_params.ring_size = MEMIF_DEFAULT_RING_SIZE; + input_params.buffer_size = MEMIF_DEFAULT_BUFFER_SIZE; + + memif_output_params_t output_params = {0}; + + if (memif_binary_api_create_memif(VPPForwarderInterface::memif_api_, + &input_params, &output_params) < 0) { + throw errors::RuntimeException( + "Error creating memif interface in the local VPP forwarder."); + } + + return output_params.sw_if_index; +} + +void VPPForwarderInterface::consumerConnection() { + hicn_consumer_input_params input = {0}; + hicn_consumer_output_params output; + + std::memset(&output, 0, sizeof(hicn_consumer_output_params)); + + input.swif = sw_if_index_; + + if (int ret = hicn_binary_api_register_cons_app( + VPPForwarderInterface::hicn_api_, &input, &output) < 0) { + throw errors::RuntimeException(hicn_binary_api_get_error_string(ret)); + } + + inet_address_.family = AF_INET; + inet_address_.prefix_len = output.src4.prefix_length; + std::memcpy(inet_address_.buffer, output.src4.ip4.as_u8, IPV4_ADDR_LEN); + + inet6_address_.family = AF_INET6; + inet6_address_.prefix_len = output.src6.prefix_length; + std::memcpy(inet6_address_.buffer, output.src6.ip6.as_u8, IPV6_ADDR_LEN); +} + +void VPPForwarderInterface::producerConnection() { + // Producer connection will be set when we set the first route. +} + +void VPPForwarderInterface::connect(bool is_consumer) { + std::lock_guard connection_lock(global_lock_); + + srand(time(NULL)); + int secret = rand() % (1 << 10); + std::stringstream app_name; + app_name << "Libtransport_" << secret; + + if (!VPPForwarderInterface::memif_api_) { + VPPForwarderInterface::api_ = vpp_binary_api_init(app_name.str().c_str()); + } + + VPPForwarderInterface::memif_api_ = + memif_binary_api_init(VPPForwarderInterface::api_); + + sw_if_index_ = getMemifConfiguration(); + + VPPForwarderInterface::hicn_api_ = + hicn_binary_api_init(VPPForwarderInterface::api_); + if (is_consumer) { + consumerConnection(); + } + + connector_.connect(memif_id_, 0); +} + +void VPPForwarderInterface::registerRoute(Prefix &prefix) { + auto &addr = prefix.toIpAddressStruct(); + + if (face_id_ == uint32_t(~0)) { + hicn_producer_input_params input; + std::memset(&input, 0, sizeof(input)); + + hicn_producer_output_params output; + std::memset(&output, 0, sizeof(output)); + + // Here we have to ask to the actual connector what is the + // memif_id, since this function should be called after the + // memif creation. + input.swif = sw_if_index_; + input.prefix.ip6.as_u64[0] = addr.as_u64[0]; + input.prefix.ip6.as_u64[1] = addr.as_u64[1]; + input.prefix.type = addr.family == AF_INET6 ? IP_TYPE_IP6 : IP_TYPE_IP4; + input.prefix.prefix_length = addr.prefix_len; + input.cs_reserved = content_store_reserved_; + + if (int ret = hicn_binary_api_register_prod_app( + VPPForwarderInterface::hicn_api_, &input, &output) < 0) { + throw errors::RuntimeException(hicn_binary_api_get_error_string(ret)); + } + + if (addr.family == AF_INET6) { + inet6_address_.prefix_len = output.prod_addr.prefix_length; + std::memcpy(inet6_address_.buffer, output.prod_addr.ip6.as_u8, + IPV6_ADDR_LEN); + } else { + inet_address_.prefix_len = output.prod_addr.prefix_length; + // The ipv4 is written in the last 4 bytes of the ipv6 address, so we need + // to copy from the byte 12 + std::memcpy(inet_address_.buffer, output.prod_addr.ip6.as_u8 + 12, + IPV4_ADDR_LEN); + } + + face_id_ = output.face_id; + } else { + hicn_producer_set_route_params params; + params.prefix.ip6.as_u64[0] = addr.as_u64[0]; + params.prefix.ip6.as_u64[1] = addr.as_u64[1]; + params.prefix.type = addr.family == AF_INET6 ? IP_TYPE_IP6 : IP_TYPE_IP4; + params.prefix.prefix_length = addr.prefix_len; + params.face_id = face_id_; + + if (int ret = hicn_binary_api_register_route( + VPPForwarderInterface::hicn_api_, ¶ms) < 0) { + throw errors::RuntimeException(hicn_binary_api_get_error_string(ret)); + } + } +} + +} // namespace core + +} // namespace transport + +#endif \ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/vpp_forwarder_interface.h b/libtransport/src/hicn/transport/core/vpp_forwarder_interface.h new file mode 100755 index 000000000..322cd1f8b --- /dev/null +++ b/libtransport/src/hicn/transport/core/vpp_forwarder_interface.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#ifdef __vpp__ + +#include +#include +#include + +#include + +namespace transport { + +namespace core { + +class VPPForwarderInterface + : public ForwarderInterface { + public: + VPPForwarderInterface(MemifConnector &connector); + + typedef MemifConnector ConnectorType; + + ~VPPForwarderInterface(); + + void connect(bool is_consumer); + + void registerRoute(Prefix &prefix); + + TRANSPORT_ALWAYS_INLINE std::uint16_t getMtu() { return interface_mtu; } + + private: + uint32_t getMemifConfiguration(); + + void consumerConnection(); + + void producerConnection(); + + static vpp_binary_api_t *api_; + static vpp_plugin_binary_api_t *memif_api_; + static vpp_plugin_binary_api_t *hicn_api_; + uint32_t memif_id_; + uint32_t sw_if_index_; + uint32_t face_id_; + static std::mutex global_lock_; + static constexpr std::uint16_t interface_mtu = 1500; +}; + +} // namespace core + +} // namespace transport + +#endif \ No newline at end of file diff --git a/libtransport/src/hicn/transport/errors/CMakeLists.txt b/libtransport/src/hicn/transport/errors/CMakeLists.txt new file mode 100755 index 000000000..1c19a9070 --- /dev/null +++ b/libtransport/src/hicn/transport/errors/CMakeLists.txt @@ -0,0 +1,29 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/not_implemented_exception.h + ${CMAKE_CURRENT_SOURCE_DIR}/invalid_ip_address_exception.h + ${CMAKE_CURRENT_SOURCE_DIR}/malformed_name_exception.h + ${CMAKE_CURRENT_SOURCE_DIR}/errors.h + ${CMAKE_CURRENT_SOURCE_DIR}/malformed_packet_exception.h + ${CMAKE_CURRENT_SOURCE_DIR}/runtime_exception.h + ${CMAKE_CURRENT_SOURCE_DIR}/tokenizer_exception.h + ${CMAKE_CURRENT_SOURCE_DIR}/null_pointer_exception.h + ${CMAKE_CURRENT_SOURCE_DIR}/malformed_ahpacket_exception.h +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/libtransport/src/hicn/transport/errors/errors.h b/libtransport/src/hicn/transport/errors/errors.h new file mode 100755 index 000000000..512e35736 --- /dev/null +++ b/libtransport/src/hicn/transport/errors/errors.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include \ No newline at end of file diff --git a/libtransport/src/hicn/transport/errors/invalid_ip_address_exception.h b/libtransport/src/hicn/transport/errors/invalid_ip_address_exception.h new file mode 100755 index 000000000..60226f576 --- /dev/null +++ b/libtransport/src/hicn/transport/errors/invalid_ip_address_exception.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace errors { + +class InvalidIpAddressException : public std::runtime_error { + public: + InvalidIpAddressException() : std::runtime_error("") {} + + virtual char const *what() const noexcept override { + return "Malformed IP address."; + } +}; + +} // end namespace errors \ No newline at end of file diff --git a/libtransport/src/hicn/transport/errors/malformed_ahpacket_exception.h b/libtransport/src/hicn/transport/errors/malformed_ahpacket_exception.h new file mode 100755 index 000000000..f0cfe0b82 --- /dev/null +++ b/libtransport/src/hicn/transport/errors/malformed_ahpacket_exception.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace errors { + +class MalformedAHPacketException : public std::runtime_error { + public: + MalformedAHPacketException() : std::runtime_error("") {} + + virtual char const *what() const noexcept override { + return "Malformed AH packet."; + } +}; + +} // end namespace errors diff --git a/libtransport/src/hicn/transport/errors/malformed_name_exception.h b/libtransport/src/hicn/transport/errors/malformed_name_exception.h new file mode 100755 index 000000000..4ef45d2e8 --- /dev/null +++ b/libtransport/src/hicn/transport/errors/malformed_name_exception.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace errors { + +class MalformedNameException : public std::runtime_error { + public: + MalformedNameException() : std::runtime_error("") {} + + virtual char const *what() const noexcept override { + return "Malformed IP address."; + } +}; + +} // end namespace errors diff --git a/libtransport/src/hicn/transport/errors/malformed_packet_exception.h b/libtransport/src/hicn/transport/errors/malformed_packet_exception.h new file mode 100755 index 000000000..ec5c97e6e --- /dev/null +++ b/libtransport/src/hicn/transport/errors/malformed_packet_exception.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace errors { + +class MalformedPacketException : public std::runtime_error { + public: + MalformedPacketException() : std::runtime_error("") {} + + char const *what() const noexcept override { return "Malformed IP packet."; } +}; + +} // end namespace errors diff --git a/libtransport/src/hicn/transport/errors/not_implemented_exception.h b/libtransport/src/hicn/transport/errors/not_implemented_exception.h new file mode 100755 index 000000000..e9869163d --- /dev/null +++ b/libtransport/src/hicn/transport/errors/not_implemented_exception.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace errors { + +class NotImplementedException : public std::logic_error { + public: + NotImplementedException() : std::logic_error("") {} + virtual char const *what() const noexcept override { + return "Function not yet implemented."; + } +}; + +} // end namespace errors diff --git a/libtransport/src/hicn/transport/errors/null_pointer_exception.h b/libtransport/src/hicn/transport/errors/null_pointer_exception.h new file mode 100755 index 000000000..bd06485ed --- /dev/null +++ b/libtransport/src/hicn/transport/errors/null_pointer_exception.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace errors { + +class NullPointerException : public std::runtime_error { + public: + NullPointerException() : std::runtime_error("") {} + + char const *what() const noexcept override { + return "Null pointer exception."; + } +}; + +} // end namespace errors diff --git a/libtransport/src/hicn/transport/errors/runtime_exception.h b/libtransport/src/hicn/transport/errors/runtime_exception.h new file mode 100755 index 000000000..ba5128a7e --- /dev/null +++ b/libtransport/src/hicn/transport/errors/runtime_exception.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +namespace errors { + +class RuntimeException : public std::runtime_error { + public: + RuntimeException() : std::runtime_error("") {} + + RuntimeException(std::string what) : runtime_error(what){}; +}; + +} // end namespace errors \ No newline at end of file diff --git a/libtransport/src/hicn/transport/errors/tokenizer_exception.h b/libtransport/src/hicn/transport/errors/tokenizer_exception.h new file mode 100755 index 000000000..76eda838e --- /dev/null +++ b/libtransport/src/hicn/transport/errors/tokenizer_exception.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace errors { + +class TokenizerException : public std::logic_error { + public: + TokenizerException() : std::logic_error("") {} + + virtual char const *what() const noexcept override { + return "No more tokens available."; + } +}; + +} // end namespace errors diff --git a/libtransport/src/hicn/transport/http/CMakeLists.txt b/libtransport/src/hicn/transport/http/CMakeLists.txt new file mode 100755 index 000000000..ddcf1fdc3 --- /dev/null +++ b/libtransport/src/hicn/transport/http/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/client_connection.cc + ${CMAKE_CURRENT_SOURCE_DIR}/request.cc + ${CMAKE_CURRENT_SOURCE_DIR}/server_publisher.cc + ${CMAKE_CURRENT_SOURCE_DIR}/server_acceptor.cc + ${CMAKE_CURRENT_SOURCE_DIR}/response.cc) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/client_connection.h + ${CMAKE_CURRENT_SOURCE_DIR}/request.h + ${CMAKE_CURRENT_SOURCE_DIR}/server_publisher.h + ${CMAKE_CURRENT_SOURCE_DIR}/server_acceptor.h + ${CMAKE_CURRENT_SOURCE_DIR}/default_values.h + ${CMAKE_CURRENT_SOURCE_DIR}/facade.h + ${CMAKE_CURRENT_SOURCE_DIR}/response.h + ${CMAKE_CURRENT_SOURCE_DIR}/message.h + ${CMAKE_CURRENT_SOURCE_DIR}/callbacks.h +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/libtransport/src/hicn/transport/http/callbacks.h b/libtransport/src/hicn/transport/http/callbacks.h new file mode 100755 index 000000000..5ca5fcbe2 --- /dev/null +++ b/libtransport/src/hicn/transport/http/callbacks.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include + +namespace transport { + +namespace http { + +enum class RC : uint8_t { + SUCCESS, + CONTENT_PUBLISHED, + ERR_UNDEFINED, +}; + +using OnHttpRequest = + std::function&, const uint8_t*, + std::size_t, int request_id)>; +using DeadlineTimerCallback = std::function; +using ReceiveCallback = std::function&)>; +using OnPayloadCallback = std::function>& payload)>; +using ContentSentCallback = + std::function; + +} // namespace http + +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/http/client_connection.cc b/libtransport/src/hicn/transport/http/client_connection.cc new file mode 100755 index 000000000..d4207bb81 --- /dev/null +++ b/libtransport/src/hicn/transport/http/client_connection.cc @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#define DEFAULT_BETA 0.99 +#define DEFAULT_GAMMA 0.07 + +namespace transport { + +namespace http { + +using namespace transport; + +HTTPClientConnection::HTTPClientConnection() + : consumer_(TransportProtocolAlgorithms::RAAQM, io_service_), + response_(std::make_shared()), + timer_(nullptr) { + consumer_.setSocketOption( + ConsumerCallbacksOptions::CONTENT_OBJECT_TO_VERIFY, + (ConsumerContentObjectVerificationCallback)std::bind( + &HTTPClientConnection::verifyData, this, std::placeholders::_1, + std::placeholders::_2)); + + consumer_.setSocketOption( + ConsumerCallbacksOptions::CONTENT_RETRIEVED, + (ConsumerContentCallback)std::bind( + &HTTPClientConnection::processPayload, this, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3)); + + consumer_.connect(); + std::shared_ptr portal; + consumer_.getSocketOption(GeneralTransportOptions::PORTAL, portal); + timer_ = std::make_unique(portal->getIoService()); +} + +HTTPClientConnection &HTTPClientConnection::get( + const std::string &url, HTTPHeaders headers, HTTPPayload payload, + std::shared_ptr response) { + return sendRequest(url, HTTPMethod::GET, headers, payload, response); +} + +HTTPClientConnection &HTTPClientConnection::sendRequest( + const std::string &url, HTTPMethod method, HTTPHeaders headers, + HTTPPayload payload, std::shared_ptr response) { + if (!response) { + response = response_; + } + + auto start = std::chrono::steady_clock::now(); + HTTPRequest request(method, url, headers, payload); + std::string name = sendRequestGetReply(request, response); + auto end = std::chrono::steady_clock::now(); + + TRANSPORT_LOGI( + "%s %s [%s] duration: %llu [usec] %zu [bytes]\n", + method_map[method].c_str(), url.c_str(), name.c_str(), + (unsigned long long)std::chrono::duration_cast( + end - start) + .count(), + response->size()); + + return *this; +} + +std::string HTTPClientConnection::sendRequestGetReply( + const HTTPRequest &request, std::shared_ptr &response) { + const std::string &request_string = request.getRequestString(); + const std::string &locator = request.getLocator(); + + // Hash it + + uint32_t locator_hash = + utils::hash::fnv32_buf(locator.c_str(), locator.size()); + uint64_t request_hash = + utils::hash::fnv64_buf(request_string.c_str(), request_string.size()); + + consumer_.setSocketOption( + ConsumerCallbacksOptions::INTEREST_OUTPUT, + (ConsumerInterestCallback)std::bind( + &HTTPClientConnection::processLeavingInterest, this, + std::placeholders::_1, std::placeholders::_2, request_string)); + + // Send content to producer piggybacking it through first interest (to fix) + + response->clear(); + + // Factor hicn name using hash + + std::stringstream stream; + + stream << std::hex << http::default_values::ipv6_first_word << ":"; + + for (uint16_t *word = (uint16_t *)&locator_hash; + std::size_t(word) < (std::size_t(&locator_hash) + sizeof(locator_hash)); + word++) { + stream << ":" << std::hex << *word; + } + + for (uint16_t *word = (uint16_t *)&request_hash; + std::size_t(word) < (std::size_t(&request_hash) + sizeof(request_hash)); + word++) { + stream << ":" << std::hex << *word; + } + + stream << "|0"; + + consumer_.consume(Name(stream.str()), *response); + + consumer_.stop(); + + return stream.str(); +} + +HTTPResponse &&HTTPClientConnection::response() { + // response_->parse(); + return std::move(*response_); +} + +void HTTPClientConnection::processPayload(ConsumerSocket &c, + std::size_t bytes_transferred, + const std::error_code &ec) { + if (ec) { + TRANSPORT_LOGE("Download failed!!"); + } +} + +bool HTTPClientConnection::verifyData( + ConsumerSocket &c, const core::ContentObject &contentObject) { + if (contentObject.getPayloadType() == PayloadType::CONTENT_OBJECT) { + TRANSPORT_LOGI("VERIFY CONTENT\n"); + } else if (contentObject.getPayloadType() == PayloadType::MANIFEST) { + TRANSPORT_LOGI("VERIFY MANIFEST\n"); + } + + return true; +} + +void HTTPClientConnection::processLeavingInterest( + ConsumerSocket &c, const core::Interest &interest, std::string &payload) { + // if (interest.getName().getSuffix() == 0) { + Interest &int2 = const_cast(interest); + int2.appendPayload((uint8_t *)payload.data(), payload.size()); + // } +} + +ConsumerSocket &HTTPClientConnection::getConsumer() { return consumer_; } + +HTTPClientConnection &HTTPClientConnection::stop() { + // This is thread safe and can be called from another thread + consumer_.stop(); + + return *this; +} + +HTTPClientConnection &HTTPClientConnection::setTimeout( + const std::chrono::seconds &timeout) { + timer_->cancel(); + timer_->expires_from_now(timeout); + timer_->async_wait([this](std::error_code ec) { + if (!ec) { + consumer_.stop(); + } + }); + + return *this; +} + +HTTPClientConnection &HTTPClientConnection::setCertificate( + const std::string &cert_path) { + if (consumer_.setSocketOption(GeneralTransportOptions::CERTIFICATE, + cert_path) == SOCKET_OPTION_NOT_SET) { + throw errors::RuntimeException("Error setting the certificate."); + } + + return *this; +} + +} // namespace http + +} // namespace transport diff --git a/libtransport/src/hicn/transport/http/client_connection.h b/libtransport/src/hicn/transport/http/client_connection.h new file mode 100755 index 000000000..f6e1fa03e --- /dev/null +++ b/libtransport/src/hicn/transport/http/client_connection.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +namespace transport { + +namespace http { + +using namespace interface; +using namespace core; + +class HTTPClientConnection { + public: + HTTPClientConnection(); + + HTTPClientConnection &get(const std::string &url, HTTPHeaders headers = {}, + HTTPPayload payload = {}, + std::shared_ptr response = nullptr); + + HTTPClientConnection &sendRequest( + const std::string &url, HTTPMethod method, HTTPHeaders headers = {}, + HTTPPayload payload = {}, + std::shared_ptr response = nullptr); + + HTTPResponse &&response(); + + HTTPClientConnection &stop(); + + interface::ConsumerSocket &getConsumer(); + + HTTPClientConnection &setTimeout(const std::chrono::seconds &timeout); + + HTTPClientConnection &setCertificate(const std::string &cert_path); + + private: + void processPayload(interface::ConsumerSocket &c, + std::size_t bytes_transferred, const std::error_code &ec); + + std::string sendRequestGetReply(const HTTPRequest &request, + std::shared_ptr &response); + + bool verifyData(interface::ConsumerSocket &c, + const core::ContentObject &contentObject); + + void processLeavingInterest(interface::ConsumerSocket &c, + const core::Interest &interest, + std::string &payload); + + asio::io_service io_service_; + + ConsumerSocket consumer_; + + std::shared_ptr response_; + + std::unique_ptr timer_; +}; + +} // end namespace http + +} // end namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/http/default_values.h b/libtransport/src/hicn/transport/http/default_values.h new file mode 100755 index 000000000..2d5a6b821 --- /dev/null +++ b/libtransport/src/hicn/transport/http/default_values.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace transport { + +namespace http { + +namespace default_values { + +const uint16_t ipv6_first_word = 0xb001; // Network byte order + +} // namespace default_values + +} // namespace http + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/http/facade.h b/libtransport/src/hicn/transport/http/facade.h new file mode 100755 index 000000000..31c2d1b8d --- /dev/null +++ b/libtransport/src/hicn/transport/http/facade.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +namespace libl4 = transport; \ No newline at end of file diff --git a/libtransport/src/hicn/transport/http/message.h b/libtransport/src/hicn/transport/http/message.h new file mode 100755 index 000000000..7d4485c90 --- /dev/null +++ b/libtransport/src/hicn/transport/http/message.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include +#include + +#define HTTP_VERSION "1.1" + +namespace transport { + +namespace http { + +typedef enum { GET, POST, PUT, PATCH, DELETE } HTTPMethod; + +static std::map method_map = { + {GET, "GET"}, {POST, "POST"}, {PUT, "PUT"}, + {PATCH, "PATCH"}, {DELETE, "DELETE"}, +}; + +typedef std::map HTTPHeaders; +typedef std::vector HTTPPayload; + +class HTTPMessage { + public: + virtual ~HTTPMessage() = default; + + virtual const HTTPHeaders &getHeaders() = 0; + + virtual const HTTPPayload &getPayload() = 0; + + virtual const std::string &getHttpVersion() const = 0; + + protected: + HTTPHeaders headers_; + HTTPPayload payload_; + std::string http_version_; +}; + +} // end namespace http + +} // end namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/http/request.cc b/libtransport/src/hicn/transport/http/request.cc new file mode 100755 index 000000000..7a63b4f75 --- /dev/null +++ b/libtransport/src/hicn/transport/http/request.cc @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace transport { + +namespace http { + +// std::map method_map + +HTTPRequest::HTTPRequest(HTTPMethod method, const std::string &url, + const HTTPHeaders &headers, + const HTTPPayload &payload) { + utils::Uri uri; + uri.parse(url); + + path_ = uri.getPath(); + query_string_ = uri.getQueryString(); + protocol_ = uri.getProtocol(); + locator_ = uri.getLocator(); + port_ = uri.getPort(); + http_version_ = HTTP_VERSION; + + headers_ = headers; + payload_ = payload; + + std::transform(locator_.begin(), locator_.end(), locator_.begin(), ::tolower); + + std::transform(protocol_.begin(), protocol_.end(), protocol_.begin(), + ::tolower); + + std::stringstream stream; + stream << method_map[method] << " " << uri.getPath() << " HTTP/" + << HTTP_VERSION << "\r\n"; + for (auto &item : headers) { + stream << item.first << ": " << item.second << "\r\n"; + } + stream << "\r\n"; + + if (payload.size() > 0) { + stream << payload.data(); + } + + request_string_ = stream.str(); +} + +const std::string &HTTPRequest::getPort() const { return port_; } + +const std::string &HTTPRequest::getLocator() const { return locator_; } + +const std::string &HTTPRequest::getProtocol() const { return protocol_; } + +const std::string &HTTPRequest::getPath() const { return path_; } + +const std::string &HTTPRequest::getQueryString() const { return query_string_; } + +const HTTPHeaders &HTTPRequest::getHeaders() { return headers_; } + +const HTTPPayload &HTTPRequest::getPayload() { return payload_; } + +const std::string &HTTPRequest::getRequestString() const { + return request_string_; +} + +const std::string &HTTPRequest::getHttpVersion() const { return http_version_; } + +} // namespace http + +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/http/request.h b/libtransport/src/hicn/transport/http/request.h new file mode 100755 index 000000000..88d67d4ad --- /dev/null +++ b/libtransport/src/hicn/transport/http/request.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include +#include +#include + +namespace transport { + +namespace http { + +class HTTPRequest : public HTTPMessage { + public: + HTTPRequest(HTTPMethod method, const std::string &url, + const HTTPHeaders &headers, const HTTPPayload &payload); + + const std::string &getQueryString() const; + + const std::string &getPath() const; + + const std::string &getProtocol() const; + + const std::string &getLocator() const; + + const std::string &getPort() const; + + const std::string &getRequestString() const; + + const HTTPHeaders &getHeaders() override; + + const HTTPPayload &getPayload() override; + + const std::string &getHttpVersion() const override; + + private: + std::string query_string_, path_, protocol_, locator_, port_; + std::string request_string_; + HTTPHeaders headers_; + HTTPPayload payload_; +}; + +} // end namespace http + +} // end namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/http/response.cc b/libtransport/src/hicn/transport/http/response.cc new file mode 100755 index 000000000..0aa9affe8 --- /dev/null +++ b/libtransport/src/hicn/transport/http/response.cc @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include + +namespace transport { + +namespace http { + +HTTPResponse::HTTPResponse() {} + +HTTPResponse::HTTPResponse(const HTTPHeaders &headers, + const HTTPPayload &payload) { + headers_ = headers; + payload_ = payload; +} + +const HTTPHeaders &HTTPResponse::getHeaders() { + parse(); + return headers_; +} + +const HTTPPayload &HTTPResponse::getPayload() { + parse(); + return payload_; +} + +bool HTTPResponse::parseHeaders() { + const char *crlf2 = "\r\n\r\n"; + auto it = + std::search(this->begin(), this->end(), crlf2, crlf2 + strlen(crlf2)); + + if (it != end()) { + std::stringstream ss; + ss.str(std::string(begin(), it)); + + std::string line; + getline(ss, line); + std::istringstream line_s(line); + std::string _http_version; + std::string http_version; + + line_s >> _http_version; + std::size_t separator; + if ((separator = _http_version.find('/')) != std::string::npos) { + if (_http_version.substr(0, separator) != "HTTP") { + return false; + } + http_version_ = + line.substr(separator + 1, _http_version.length() - separator - 1); + } else { + return false; + } + + std::string status_code, status_string; + + line_s >> status_code_; + line_s >> status_string; + + auto _it = std::search(line.begin(), line.end(), status_string.begin(), + status_string.end()); + + status_string_ = std::string(_it, line.end() - 1); + + std::size_t param_end; + std::size_t value_start; + while (getline(ss, line)) { + if ((param_end = line.find(':')) != std::string::npos) { + value_start = param_end + 1; + if ((value_start) < line.size()) { + if (line[value_start] == ' ') { + value_start++; + } + if (value_start < line.size()) { + headers_[line.substr(0, param_end)] = + line.substr(value_start, line.size() - value_start - 1); + } + } + } else { + return false; + } + } + } + + return true; +} + +void HTTPResponse::parse() { + if (!parseHeaders()) { + throw errors::RuntimeException("Malformed HTTP response"); + } + + if (payload_.empty()) { + const char *crlf2 = "\r\n\r\n"; + auto it = + std::search(this->begin(), this->end(), crlf2, crlf2 + strlen(crlf2)); + + if (it != this->end()) { + erase(begin(), it + strlen(crlf2)); + payload_ = std::move(*dynamic_cast *>(this)); + } + } +} + +const std::string &HTTPResponse::getStatusCode() const { return status_code_; } + +const std::string &HTTPResponse::getStatusString() const { + return status_string_; +} + +const std::string &HTTPResponse::getHttpVersion() const { + return http_version_; +} + +} // namespace http + +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/http/response.h b/libtransport/src/hicn/transport/http/response.h new file mode 100755 index 000000000..e7dec8c40 --- /dev/null +++ b/libtransport/src/hicn/transport/http/response.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace transport { + +namespace http { + +class HTTPResponse : public HTTPMessage, public utils::SharableVector { + public: + HTTPResponse(const HTTPHeaders &headers, const HTTPPayload &payload); + + HTTPResponse(); + + const HTTPHeaders &getHeaders() override; + + const HTTPPayload &getPayload() override; + + const std::string &getStatusCode() const; + + const std::string &getStatusString() const; + + const std::string &getHttpVersion() const override; + + void parse(); + + private: + bool parseHeaders(); + + private: + std::string status_code_; + std::string status_string_; +}; + +} // end namespace http + +} // end namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/http/server_acceptor.cc b/libtransport/src/hicn/transport/http/server_acceptor.cc new file mode 100755 index 000000000..717dfb642 --- /dev/null +++ b/libtransport/src/hicn/transport/http/server_acceptor.cc @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +namespace transport { + +namespace http { + +HTTPServerAcceptor::HTTPServerAcceptor(std::string &&server_locator, + OnHttpRequest callback) + : HTTPServerAcceptor(server_locator, callback) {} + +HTTPServerAcceptor::HTTPServerAcceptor(std::string &server_locator, + OnHttpRequest callback) + : callback_(callback) { + utils::Uri uri; + + uri.parseProtocolAndLocator(server_locator); + std::string protocol = uri.getProtocol(); + std::string locator = uri.getLocator(); + + std::transform(locator.begin(), locator.end(), locator.begin(), ::tolower); + + std::transform(protocol.begin(), protocol.end(), protocol.begin(), ::tolower); + + if (protocol != "http") { + throw errors::RuntimeException( + "Malformed server_locator. The locator format should be in the form " + "http://locator"); + } + + uint32_t locator_hash = + utils::hash::fnv32_buf(locator.c_str(), locator.size()); + + std::stringstream stream; + stream << std::hex << http::default_values::ipv6_first_word << ":0000"; + + for (uint16_t *word = (uint16_t *)&locator_hash; + std::size_t(word) < (std::size_t(&locator_hash) + sizeof(locator_hash)); + word++) { + stream << ":" << std::hex << *word; + } + + stream << "::0"; + + std::string network = stream.str(); + + core::Prefix acceptor_namespace(network, 64); + + std::string producer_identity = "acceptor_producer"; + acceptor_producer_ = std::make_shared( + io_service_); /*, + utils::Identity::generateIdentity(producer_identity));*/ + acceptor_producer_->registerPrefix(acceptor_namespace); +} + +void HTTPServerAcceptor::listen(bool async) { + acceptor_producer_->setSocketOption( + ProducerCallbacksOptions::INTEREST_INPUT, + (ProducerInterestCallback)bind( + &HTTPServerAcceptor::processIncomingInterest, this, + std::placeholders::_1, std::placeholders::_2)); + acceptor_producer_->connect(); + + if (!async) { + acceptor_producer_->serveForever(); + } +} + +void HTTPServerAcceptor::processIncomingInterest(ProducerSocket &p, + const Interest &interest) { + // Temporary solution. With + utils::Array payload = interest.getPayload(); + + int request_id = utils::hash::fnv32_buf(payload.data(), payload.length()); + + if (publishers_.find(request_id) != publishers_.end()) { + if (publishers_[request_id]) { + publishers_[request_id]->getProducer().onInterest(interest); + return; + } + } + + publishers_[request_id] = + std::make_shared(interest.getName()); + callback_(publishers_[request_id], (uint8_t *)payload.data(), + payload.length(), request_id); +} + +std::map> + &HTTPServerAcceptor::getPublishers() { + return publishers_; +} + +} // namespace http + +} // namespace transport diff --git a/libtransport/src/hicn/transport/http/server_acceptor.h b/libtransport/src/hicn/transport/http/server_acceptor.h new file mode 100755 index 000000000..549962414 --- /dev/null +++ b/libtransport/src/hicn/transport/http/server_acceptor.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace transport { + +namespace http { + +class HTTPServerAcceptor { + friend class HTTPServerPublisher; + + public: + HTTPServerAcceptor(std::string &&server_locator, OnHttpRequest callback); + HTTPServerAcceptor(std::string &server_locator, OnHttpRequest callback); + + void listen(bool async); + + std::map> &getPublishers(); + + // void asyncSendResponse(); + + // HTTPClientConnection& get(std::string &url, HTTPHeaders headers = {}, + // HTTPPayload payload = {}); + // + // HTTPResponse&& response(); + + private: + void processIncomingInterest(ProducerSocket &p, const Interest &interest); + + OnHttpRequest callback_; + asio::io_service io_service_; + std::shared_ptr acceptor_producer_; + + std::map> publishers_; +}; + +} // end namespace http + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/http/server_publisher.cc b/libtransport/src/hicn/transport/http/server_publisher.cc new file mode 100755 index 000000000..012f36091 --- /dev/null +++ b/libtransport/src/hicn/transport/http/server_publisher.cc @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace transport { + +namespace http { + +HTTPServerPublisher::HTTPServerPublisher(const core::Name &content_name) + : content_name_(content_name, true) { + std::string identity = "acceptor_producer"; + producer_ = std::make_unique(io_service_); + // utils::Identity::generateIdentity(identity)); + core::Prefix publisher_prefix(content_name_, 128); + producer_->registerPrefix(publisher_prefix); +} + +HTTPServerPublisher::~HTTPServerPublisher() { + if (timer_) { + this->timer_->cancel(); + } +} + +HTTPServerPublisher &HTTPServerPublisher::attachPublisher() { + // Create a new publisher + producer_->setSocketOption(GeneralTransportOptions::DATA_PACKET_SIZE, + 1410_U32); + producer_->connect(); + return *this; +} + +HTTPServerPublisher &HTTPServerPublisher::setTimeout( + const std::chrono::milliseconds &timeout, bool timeout_renewal) { + std::shared_ptr portal; + producer_->getSocketOption(GeneralTransportOptions::PORTAL, portal); + timer_ = + std::make_unique(portal->getIoService(), timeout); + + wait_callback_ = [this](const std::error_code &e) { + if (!e) { + producer_->stop(); + } + }; + + if (timeout_renewal) { + interest_enter_callback_ = [this, timeout](ProducerSocket &p, + const Interest &interest) { + this->timer_->cancel(); + this->timer_->expires_from_now(timeout); + this->timer_->async_wait(wait_callback_); + }; + + producer_->setSocketOption( + ProducerCallbacksOptions::CACHE_HIT, + (ProducerInterestCallback)interest_enter_callback_); + } + + timer_->async_wait(wait_callback_); + + return *this; +} + +void HTTPServerPublisher::publishContent( + const uint8_t *buf, size_t buffer_size, + std::chrono::milliseconds content_lifetime, bool is_last) { + if (producer_) { + producer_->setSocketOption( + GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME, + static_cast(content_lifetime.count())); + producer_->produce(content_name_, buf, buffer_size, is_last); + // producer_->setSocketOption(ProducerCallbacksOptions::CACHE_MISS, + // [this](ProducerSocket &p, const + // core::Interest &interest){ + // producer_->stop(); + // }); + } +} + +template +void HTTPServerPublisher::asyncPublishContent( + const uint8_t *buf, size_t buffer_size, + std::chrono::milliseconds content_lifetime, Handler &&handler, + bool is_last) { + if (producer_) { + producer_->setSocketOption( + GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME, + static_cast(content_lifetime.count())); + producer_->asyncProduce(content_name_, buf, buffer_size, + std::forward(handler), is_last); + } +} + +void HTTPServerPublisher::serveClients() { producer_->serveForever(); } + +void HTTPServerPublisher::stop() { + std::shared_ptr portal_ptr; + producer_->getSocketOption(GeneralTransportOptions::PORTAL, portal_ptr); + portal_ptr->getIoService().stop(); +} + +ProducerSocket &HTTPServerPublisher::getProducer() { return *producer_; } + +void HTTPServerPublisher::setPublisherName(std::string &name, + std::string &mask) { + // Name represents the last 64 bits of the ipv6 address. + // It is an ipv6 address with the first 64 bits set to 0 + uint16_t i; + std::string s = content_name_.toString(); + std::shared_ptr sockaddr = content_name_.getAddress(); + in6_addr name_ipv6 = ((core::Sockaddr6 *)sockaddr.get())->sin6_addr; + + in6_addr bitmask, new_address, _name; + + if (inet_pton(AF_INET6, mask.c_str(), &bitmask) != 1) { + throw errors::RuntimeException("Error during conversion to ipv6 address."); + } + + if (inet_pton(AF_INET6, name.c_str(), &_name) != 1) { + throw errors::RuntimeException("Error during conversion to ipv6 address."); + } + + for (i = 0; i < sizeof(new_address.s6_addr); i++) { + new_address.s6_addr[i] = name_ipv6.s6_addr[i] & bitmask.s6_addr[i]; + } + + for (i = 0; i < sizeof(new_address.s6_addr); i++) { + new_address.s6_addr[i] |= _name.s6_addr[i] & ~bitmask.s6_addr[i]; + } + + // Effectively change the name + char str[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &new_address, str, INET6_ADDRSTRLEN); + std::string str2(str); + + core::Name new_name(str2, 0); + + // If the new name differs from the one required by the consumer part, send a + // manifest + if (!new_name.equals(content_name_, false)) { + // Publish manifest pointing to the new name + + auto manifest = + std::make_shared(content_name_.setSuffix(0)); + + content_name_ = core::Name(str2, 0); + + // manifest->setNameList(content_name_); + manifest->setLifetime(4000 * 1000); + manifest->encode(); + producer_->produce(*manifest); + + core::Prefix ns(content_name_, 128); + producer_->registerPrefix(ns); + } +} + +} // namespace http + +} // namespace transport diff --git a/libtransport/src/hicn/transport/http/server_publisher.h b/libtransport/src/hicn/transport/http/server_publisher.h new file mode 100755 index 000000000..91f7e43e9 --- /dev/null +++ b/libtransport/src/hicn/transport/http/server_publisher.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include +#include + +namespace transport { + +namespace http { + +using namespace interface; +using namespace core; + +class HTTPServerPublisher { + public: + HTTPServerPublisher(const core::Name &content_name); + + ~HTTPServerPublisher(); + + void publishContent(const uint8_t *buf, size_t buffer_size, + std::chrono::milliseconds content_lifetime, bool is_last); + + template + void asyncPublishContent(const uint8_t *buf, size_t buffer_size, + std::chrono::milliseconds content_lifetime, + Handler &&handler, bool is_last); + + void serveClients(); + + void stop(); + + ProducerSocket &getProducer(); + + HTTPServerPublisher &setTimeout(const std::chrono::milliseconds &timeout, + bool timeout_renewal); + + HTTPServerPublisher &attachPublisher(); + + void setPublisherName(std::string &name, std::string &mask); + + private: + Name content_name_; + std::unique_ptr timer_; + asio::io_service io_service_; + std::unique_ptr producer_; + ProducerInterestCallback interest_enter_callback_; + utils::UserCallback wait_callback_; + + utils::SharableVector receive_buffer_; +}; + +} // end namespace http + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/CMakeLists.txt b/libtransport/src/hicn/transport/interfaces/CMakeLists.txt new file mode 100755 index 000000000..cbf371bac --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/socket.h + ${CMAKE_CURRENT_SOURCE_DIR}/socket_consumer.h + ${CMAKE_CURRENT_SOURCE_DIR}/socket_producer.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_socket_consumer.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_socket_producer.h + ${CMAKE_CURRENT_SOURCE_DIR}/async_transport.h + ${CMAKE_CURRENT_SOURCE_DIR}/full_duplex_socket.h + ${CMAKE_CURRENT_SOURCE_DIR}/publication_options.h + ${CMAKE_CURRENT_SOURCE_DIR}/socket_options_default_values.h + ${CMAKE_CURRENT_SOURCE_DIR}/socket_options_keys.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/full_duplex_socket.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_socket_consumer.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_socket_producer.cc + ${CMAKE_CURRENT_SOURCE_DIR}/socket_producer.cc + ${CMAKE_CURRENT_SOURCE_DIR}/socket_consumer.cc +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/libtransport/src/hicn/transport/interfaces/async_transport.h b/libtransport/src/hicn/transport/interfaces/async_transport.h new file mode 100755 index 000000000..492b4ec26 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/async_transport.h @@ -0,0 +1,640 @@ + +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include +#include + +namespace transport { + +namespace interface { + +/* + * flags given by the application for write* calls + */ +enum class WriteFlags : uint32_t { + NONE = 0x00, + /* + * Whether to delay the output until a subsequent non-corked write. + * (Note: may not be supported in all subclasses or on all platforms.) + */ + CORK = 0x01, + /* + * for a socket that has ACK latency enabled, it will cause the kernel + * to fire a TCP ESTATS event when the last byte of the given write call + * will be acknowledged. + */ + EOR = 0x02, + /* + * this indicates that only the write side of socket should be shutdown + */ + WRITE_SHUTDOWN = 0x04, + /* + * use msg zerocopy if allowed + */ + WRITE_MSG_ZEROCOPY = 0x08, +}; + +/* + * union operator + */ +TRANSPORT_ALWAYS_INLINE WriteFlags operator|(WriteFlags a, WriteFlags b) { + return static_cast(static_cast(a) | + static_cast(b)); +} + +/* + * compound assignment union operator + */ +TRANSPORT_ALWAYS_INLINE WriteFlags &operator|=(WriteFlags &a, WriteFlags b) { + a = a | b; + return a; +} + +/* + * intersection operator + */ +TRANSPORT_ALWAYS_INLINE WriteFlags operator&(WriteFlags a, WriteFlags b) { + return static_cast(static_cast(a) & + static_cast(b)); +} + +/* + * compound assignment intersection operator + */ +TRANSPORT_ALWAYS_INLINE WriteFlags &operator&=(WriteFlags &a, WriteFlags b) { + a = a & b; + return a; +} + +/* + * exclusion parameter + */ +TRANSPORT_ALWAYS_INLINE WriteFlags operator~(WriteFlags a) { + return static_cast(~static_cast(a)); +} + +/* + * unset operator + */ +TRANSPORT_ALWAYS_INLINE WriteFlags unSet(WriteFlags a, WriteFlags b) { + return a & ~b; +} + +/* + * inclusion operator + */ +TRANSPORT_ALWAYS_INLINE bool isSet(WriteFlags a, WriteFlags b) { + return (a & b) == b; +} + +class ConnectCallback { + public: + virtual ~ConnectCallback() = default; + + /** + * connectSuccess() will be invoked when the connection has been + * successfully established. + */ + virtual void connectSuccess() noexcept = 0; + + /** + * connectErr() will be invoked if the connection attempt fails. + * + * @param ex An exception describing the error that occurred. + */ + virtual void connectErr(const std::error_code ec) noexcept = 0; +}; + +/** + * AsyncSocket defines an asynchronous API for streaming I/O. + * + * This class provides an API to for asynchronously waiting for data + * on a streaming transport, and for asynchronously sending data. + * + * The APIs for reading and writing are intentionally asymmetric. Waiting for + * data to read is a persistent API: a callback is installed, and is notified + * whenever new data is available. It continues to be notified of new events + * until it is uninstalled. + * + * AsyncSocket does not provide read timeout functionality, because it + * typically cannot determine when the timeout should be active. Generally, a + * timeout should only be enabled when processing is blocked waiting on data + * from the remote endpoint. For server-side applications, the timeout should + * not be active if the server is currently processing one or more outstanding + * requests on this transport. For client-side applications, the timeout + * should not be active if there are no requests pending on the transport. + * Additionally, if a client has multiple pending requests, it will ususally + * want a separate timeout for each request, rather than a single read timeout. + * + * The write API is fairly intuitive: a user can request to send a block of + * data, and a callback will be informed once the entire block has been + * transferred to the kernel, or on error. AsyncSocket does provide a send + * timeout, since most callers want to give up if the remote end stops + * responding and no further progress can be made sending the data. + */ +class AsyncSocket { + public: + /** + * Close the transport. + * + * This gracefully closes the transport, waiting for all pending write + * requests to complete before actually closing the underlying transport. + * + * If a read callback is set, readEOF() will be called immediately. If there + * are outstanding write requests, the close will be delayed until all + * remaining writes have completed. No new writes may be started after + * close() has been called. + */ + virtual void close() = 0; + + /** + * Close the transport immediately. + * + * This closes the transport immediately, dropping any outstanding data + * waiting to be written. + * + * If a read callback is set, readEOF() will be called immediately. + * If there are outstanding write requests, these requests will be aborted + * and writeError() will be invoked immediately on all outstanding write + * callbacks. + */ + virtual void closeNow() = 0; + + /** + * Perform a half-shutdown of the write side of the transport. + * + * The caller should not make any more calls to write() or writev() after + * shutdownWrite() is called. Any future write attempts will fail + * immediately. + * + * Not all transport types support half-shutdown. If the underlying + * transport does not support half-shutdown, it will fully shutdown both the + * read and write sides of the transport. (Fully shutting down the socket is + * better than doing nothing at all, since the caller may rely on the + * shutdownWrite() call to notify the other end of the connection that no + * more data can be read.) + * + * If there is pending data still waiting to be written on the transport, + * the actual shutdown will be delayed until the pending data has been + * written. + * + * Note: There is no corresponding shutdownRead() equivalent. Simply + * uninstall the read callback if you wish to stop reading. (On TCP sockets + * at least, shutting down the read side of the socket is a no-op anyway.) + */ + virtual void shutdownWrite() = 0; + + /** + * Perform a half-shutdown of the write side of the transport. + * + * shutdownWriteNow() is identical to shutdownWrite(), except that it + * immediately performs the shutdown, rather than waiting for pending writes + * to complete. Any pending write requests will be immediately failed when + * shutdownWriteNow() is called. + */ + virtual void shutdownWriteNow() = 0; + + /** + * Determine if transport is open and ready to read or write. + * + * Note that this function returns false on EOF; you must also call error() + * to distinguish between an EOF and an error. + * + * @return true iff the transport is open and ready, false otherwise. + */ + virtual bool good() const = 0; + + /** + * Determine if the transport is readable or not. + * + * @return true iff the transport is readable, false otherwise. + */ + virtual bool readable() const = 0; + + /** + * Determine if the transport is writable or not. + * + * @return true iff the transport is writable, false otherwise. + */ + virtual bool writable() const { + // By default return good() - leave it to implementers to override. + return good(); + } + + /** + * Determine if the there is pending data on the transport. + * + * @return true iff the if the there is pending data, false otherwise. + */ + virtual bool isPending() const { return readable(); } + + /** + * Determine if transport is connected to the endpoint + * + * @return false iff the transport is connected, otherwise true + */ + virtual bool connected() const = 0; + + /** + * Determine if an error has occurred with this transport. + * + * @return true iff an error has occurred (not EOF). + */ + virtual bool error() const = 0; + + // /** + // * Attach the transport to a EventBase. + // * + // * This may only be called if the transport is not currently attached to a + // * EventBase (by an earlier call to detachEventBase()). + // * + // * This method must be invoked in the EventBase's thread. + // */ + // virtual void attachEventBase(EventBase* eventBase) = 0; + + // /** + // * Detach the transport from its EventBase. + // * + // * This may only be called when the transport is idle and has no reads or + // * writes pending. Once detached, the transport may not be used again + // until + // * it is re-attached to a EventBase by calling attachEventBase(). + // * + // * This method must be called from the current EventBase's thread. + // */ + // virtual void detachEventBase() = 0; + + // /** + // * Determine if the transport can be detached. + // * + // * This method must be called from the current EventBase's thread. + // */ + // virtual bool isDetachable() const = 0; + + /** + * Set the send timeout. + * + * If write requests do not make any progress for more than the specified + * number of milliseconds, fail all pending writes and close the transport. + * + * If write requests are currently pending when setSendTimeout() is called, + * the timeout interval is immediately restarted using the new value. + * + * @param milliseconds The timeout duration, in milliseconds. If 0, no + * timeout will be used. + */ + virtual void setSendTimeout(uint32_t milliseconds) = 0; + + /** + * Get the send timeout. + * + * @return Returns the current send timeout, in milliseconds. A return value + * of 0 indicates that no timeout is set. + */ + virtual uint32_t getSendTimeout() const = 0; + + virtual void connect(ConnectCallback *callback, + const core::Prefix &prefix_) = 0; + + // /** + // * Get the address of the local endpoint of this transport. + // * + // * This function may throw AsyncSocketException on error. + // * + // * @param address The local address will be stored in the specified + // * SocketAddress. + // */ + // virtual void getLocalAddress(* address) const = 0; + + virtual size_t getAppBytesWritten() const = 0; + virtual size_t getRawBytesWritten() const = 0; + virtual size_t getAppBytesReceived() const = 0; + virtual size_t getRawBytesReceived() const = 0; + + class BufferCallback { + public: + virtual ~BufferCallback() {} + virtual void onEgressBuffered() = 0; + virtual void onEgressBufferCleared() = 0; + }; + + ~AsyncSocket() = default; +}; + +class AsyncAcceptor { + public: + class AcceptCallback { + public: + virtual ~AcceptCallback() = default; + + /** + * connectionAccepted() is called whenever a new client connection is + * received. + * + * The AcceptCallback will remain installed after connectionAccepted() + * returns. + * + * @param fd The newly accepted client socket. The AcceptCallback + * assumes ownership of this socket, and is responsible + * for closing it when done. The newly accepted file + * descriptor will have already been put into + * non-blocking mode. + * @param clientAddr A reference to a SocketAddress struct containing the + * client's address. This struct is only guaranteed to + * remain valid until connectionAccepted() returns. + */ + virtual void connectionAccepted( + const core::Name &subscriber_name) noexcept = 0; + + /** + * acceptError() is called if an error occurs while accepting. + * + * The AcceptCallback will remain installed even after an accept error, + * as the errors are typically somewhat transient, such as being out of + * file descriptors. The server socket must be explicitly stopped if you + * wish to stop accepting after an error. + * + * @param ex An exception representing the error. + */ + virtual void acceptError(const std::exception &ex) noexcept = 0; + + /** + * acceptStarted() will be called in the callback's EventBase thread + * after this callback has been added to the AsyncServerSocket. + * + * acceptStarted() will be called before any calls to connectionAccepted() + * or acceptError() are made on this callback. + * + * acceptStarted() makes it easier for callbacks to perform initialization + * inside the callback thread. (The call to addAcceptCallback() must + * always be made from the AsyncServerSocket's primary EventBase thread. + * acceptStarted() provides a hook that will always be invoked in the + * callback's thread.) + * + * Note that the call to acceptStarted() is made once the callback is + * added, regardless of whether or not the AsyncServerSocket is actually + * accepting at the moment. acceptStarted() will be called even if the + * AsyncServerSocket is paused when the callback is added (including if + * the initial call to startAccepting() on the AsyncServerSocket has not + * been made yet). + */ + virtual void acceptStarted() noexcept {} + + /** + * acceptStopped() will be called when this AcceptCallback is removed from + * the AsyncServerSocket, or when the AsyncServerSocket is destroyed, + * whichever occurs first. + * + * No more calls to connectionAccepted() or acceptError() will be made + * after acceptStopped() is invoked. + */ + virtual void acceptStopped() noexcept {} + }; + + /** + * Wait for subscribers + * + */ + virtual void waitForSubscribers(AcceptCallback *cb) = 0; +}; + +class AsyncReader { + public: + class ReadCallback { + public: + virtual ~ReadCallback() = default; + + /** + * When data becomes available, getReadBuffer() will be invoked to get the + * buffer into which data should be read. + * + * This method allows the ReadCallback to delay buffer allocation until + * data becomes available. This allows applications to manage large + * numbers of idle connections, without having to maintain a separate read + * buffer for each idle connection. + * + * It is possible that in some cases, getReadBuffer() may be called + * multiple times before readDataAvailable() is invoked. In this case, the + * data will be written to the buffer returned from the most recent call to + * readDataAvailable(). If the previous calls to readDataAvailable() + * returned different buffers, the ReadCallback is responsible for ensuring + * that they are not leaked. + * + * If getReadBuffer() throws an exception, returns a nullptr buffer, or + * returns a 0 length, the ReadCallback will be uninstalled and its + * readError() method will be invoked. + * + * getReadBuffer() is not allowed to change the transport state before it + * returns. (For example, it should never uninstall the read callback, or + * set a different read callback.) + * + * @param bufReturn getReadBuffer() should update *bufReturn to contain the + * address of the read buffer. This parameter will never + * be nullptr. + * @param lenReturn getReadBuffer() should update *lenReturn to contain the + * maximum number of bytes that may be written to the read + * buffer. This parameter will never be nullptr. + * + * + * XXX TODO this does not seems to be completely true Checlk i/. + */ + virtual void getReadBuffer(void **bufReturn, size_t *lenReturn) = 0; + + /** + * readDataAvailable() will be invoked when data has been successfully read + * into the buffer returned by the last call to getReadBuffer(). + * + * The read callback remains installed after readDataAvailable() returns. + * It must be explicitly uninstalled to stop receiving read events. + * getReadBuffer() will be called at least once before each call to + * readDataAvailable(). getReadBuffer() will also be called before any + * call to readEOF(). + * + * @param len The number of bytes placed in the buffer. + */ + + virtual void readDataAvailable(size_t len) noexcept = 0; + + /** + * When data becomes available, isBufferMovable() will be invoked to figure + * out which API will be used, readBufferAvailable() or + * readDataAvailable(). If isBufferMovable() returns true, that means + * ReadCallback supports the IOBuf ownership transfer and + * readBufferAvailable() will be used. Otherwise, not. + + * By default, isBufferMovable() always return false. If + * readBufferAvailable() is implemented and to be invoked, You should + * overwrite isBufferMovable() and return true in the inherited class. + * + * This method allows the AsyncSocket/AsyncSSLSocket do buffer allocation by + * itself until data becomes available. Compared with the pre/post buffer + * allocation in getReadBuffer()/readDataAvailabe(), readBufferAvailable() + * has two advantages. First, this can avoid memcpy. E.g., in + * AsyncSSLSocket, the decrypted data was copied from the openssl internal + * buffer to the readbuf buffer. With the buffer ownership transfer, the + * internal buffer can be directly "moved" to ReadCallback. Second, the + * memory allocation can be more precise. The reason is + * AsyncSocket/AsyncSSLSocket can allocate the memory of precise size + * because they have more context about the available data than + * ReadCallback. Think about the getReadBuffer() pre-allocate 4072 bytes + * buffer, but the available data is always 16KB (max OpenSSL record size). + */ + + virtual bool isBufferMovable() noexcept { return false; } + + /** + * Suggested buffer size, allocated for read operations, + * if callback is movable and supports folly::IOBuf + */ + + virtual size_t maxBufferSize() const { + return 64 * 1024; // 64K + } + + /** + * readBufferAvailable() will be invoked when data has been successfully + * read. + * + * Note that only either readBufferAvailable() or readDataAvailable() will + * be invoked according to the return value of isBufferMovable(). The timing + * and aftereffect of readBufferAvailable() are the same as + * readDataAvailable() + * + * @param readBuf The unique pointer of read buffer. + */ + + // virtual void readBufferAvailable(uint8_t** buffer, std::size_t + // *buf_length) noexcept {} + + virtual void readBufferAvailable( + utils::SharableVector &&buffer) noexcept {} + + // virtual void readBufferAvailable(utils::SharableBuffer&& buffer) + // noexcept {} + + /** + * readEOF() will be invoked when the transport is closed. + * + * The read callback will be automatically uninstalled immediately before + * readEOF() is invoked. + */ + virtual void readEOF() noexcept = 0; + + /** + * readError() will be invoked if an error occurs reading from the + * transport. + * + * The read callback will be automatically uninstalled immediately before + * readError() is invoked. + * + * @param ex An exception describing the error that occurred. + */ + virtual void readErr(const std::error_code ec) noexcept = 0; + }; + + // Read methods that aren't part of AsyncTransport. + virtual void setReadCB(ReadCallback *callback) = 0; + virtual ReadCallback *getReadCallback() const = 0; + + protected: + virtual ~AsyncReader() = default; +}; + +class AsyncWriter { + public: + class WriteCallback { + public: + virtual ~WriteCallback() = default; + + /** + * writeSuccess() will be invoked when all of the data has been + * successfully written. + * + * Note that this mainly signals that the buffer containing the data to + * write is no longer needed and may be freed or re-used. It does not + * guarantee that the data has been fully transmitted to the remote + * endpoint. For example, on socket-based transports, writeSuccess() only + * indicates that the data has been given to the kernel for eventual + * transmission. + */ + virtual void writeSuccess() noexcept = 0; + + /** + * writeError() will be invoked if an error occurs writing the data. + * + * @param bytesWritten The number of bytes that were successfull + * @param ex An exception describing the error that occurred. + */ + virtual void writeErr(size_t bytesWritten) noexcept = 0; + }; + + /** + * If you supply a non-null WriteCallback, exactly one of writeSuccess() + * or writeErr() will be invoked when the write completes. If you supply + * the same WriteCallback object for multiple write() calls, it will be + * invoked exactly once per call. The only way to cancel outstanding + * write requests is to close the socket (e.g., with closeNow() or + * shutdownWriteNow()). When closing the socket this way, writeErr() will + * still be invoked once for each outstanding write operation. + */ + virtual void write(WriteCallback *callback, const void *buf, size_t bytes, + const PublicationOptions &options, + WriteFlags flags = WriteFlags::NONE) = 0; + + /** + * If you supply a non-null WriteCallback, exactly one of writeSuccess() + * or writeErr() will be invoked when the write completes. If you supply + * the same WriteCallback object for multiple write() calls, it will be + * invoked exactly once per call. The only way to cancel outstanding + * write requests is to close the socket (e.g., with closeNow() or + * shutdownWriteNow()). When closing the socket this way, writeErr() will + * still be invoked once for each outstanding write operation. + */ + virtual void write(WriteCallback *callback, + utils::SharableVector &&output_buffer, + const PublicationOptions &options, + WriteFlags flags = WriteFlags::NONE) = 0; + + // /** + // * If you supply a non-null WriteCallback, exactly one of writeSuccess() + // * or writeErr() will be invoked when the write completes. If you supply + // * the same WriteCallback object for multiple write() calls, it will be + // * invoked exactly once per call. The only way to cancel outstanding + // * write requests is to close the socket (e.g., with closeNow() or + // * shutdownWriteNow()). When closing the socket this way, writeErr() will + // * still be invoked once for each outstanding write operation. + // */ + // virtual void writeChain( + // WriteCallback* callback, + // std::unique_ptr&& buf, + // WriteFlags flags = WriteFlags::NONE) = 0; + + virtual void setWriteCB(WriteCallback *callback) = 0; + virtual WriteCallback *getWriteCallback() const = 0; + + protected: + virtual ~AsyncWriter() = default; +}; + +} // namespace interface + +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/interfaces/full_duplex_socket.cc b/libtransport/src/hicn/transport/interfaces/full_duplex_socket.cc new file mode 100755 index 000000000..7b6342262 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/full_duplex_socket.cc @@ -0,0 +1,490 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include + +namespace transport { + +namespace interface { + +static const std::string producer_identity = "producer_socket"; + +AsyncFullDuplexSocket::AsyncFullDuplexSocket(const Prefix &locator) + : AsyncFullDuplexSocket(locator, internal_io_service_) {} + +AsyncFullDuplexSocket::AsyncFullDuplexSocket(const Prefix &locator, + asio::io_service &io_service) + : locator_(locator), + incremental_suffix_(0), + io_service_(io_service), + work_(io_service), + producer_(std::make_unique(io_service_)), + consumer_(std::make_unique( + TransportProtocolAlgorithms::RAAQM /* , io_service_ */)), + read_callback_(nullptr), + write_callback_(nullptr), + connect_callback_(nullptr), + accept_callback_(nullptr), + internal_connect_callback_(new OnConnectCallback(*this)), + internal_signal_callback_(new OnSignalCallback(*this)), + send_timeout_milliseconds_(~0), + counters_({0}), + receive_buffer_(std::make_shared>()) { + using namespace transport; + using namespace std::placeholders; + producer_->registerPrefix(locator); + + producer_->setSocketOption( + ProducerCallbacksOptions::CACHE_MISS, + std::bind(&AsyncFullDuplexSocket::onControlInterest, this, _1, _2)); + + producer_->setSocketOption(GeneralTransportOptions::OUTPUT_BUFFER_SIZE, + uint32_t{150000}); + + producer_->setSocketOption( + ProducerCallbacksOptions::CONTENT_PRODUCED, + std::bind(&AsyncFullDuplexSocket::onContentProduced, this, _1, _2, _3)); + + producer_->connect(); + + consumer_->setSocketOption(ConsumerCallbacksOptions::CONTENT_OBJECT_TO_VERIFY, + (ConsumerContentObjectVerificationCallback)[]( + ConsumerSocket & s, const ContentObject &c) + ->bool { return true; }); + + consumer_->setSocketOption( + ConsumerCallbacksOptions::CONTENT_RETRIEVED, + std::bind(&AsyncFullDuplexSocket::onContentRetrieved, this, _1, _2, _3)); + + consumer_->setSocketOption(GeneralTransportOptions::MAX_INTEREST_RETX, + uint32_t{4}); + + consumer_->connect(); +} + +void AsyncFullDuplexSocket::close() { + this->consumer_->stop(); + this->producer_->stop(); +} + +void AsyncFullDuplexSocket::closeNow() { close(); } + +void AsyncFullDuplexSocket::shutdownWrite() { producer_->stop(); } + +void AsyncFullDuplexSocket::shutdownWriteNow() { shutdownWrite(); } + +bool AsyncFullDuplexSocket::good() const { return true; } + +bool AsyncFullDuplexSocket::readable() const { + // TODO return status of consumer socket + return true; +} + +bool AsyncFullDuplexSocket::writable() const { + // TODO return status of producer socket + return true; +} + +bool AsyncFullDuplexSocket::isPending() const { + // TODO save if there are production operation in the ops queue + // in producer socket + return true; +} + +bool AsyncFullDuplexSocket::connected() const { + // No real connection here (ICN world). Return good + return good(); +} + +bool AsyncFullDuplexSocket::error() const { return !good(); } + +void AsyncFullDuplexSocket::setSendTimeout(uint32_t milliseconds) { + // TODO if production takes too much to complete + // let's abort the operation. + + // Normally with hicn this should be done for content + // pull, not for production. + + send_timeout_milliseconds_ = milliseconds; +} + +uint32_t AsyncFullDuplexSocket::getSendTimeout() const { + return send_timeout_milliseconds_; +} + +size_t AsyncFullDuplexSocket::getAppBytesWritten() const { + return counters_.app_bytes_written_; +} + +size_t AsyncFullDuplexSocket::getRawBytesWritten() const { return 0; } + +size_t AsyncFullDuplexSocket::getAppBytesReceived() const { + return counters_.app_bytes_read_; +} + +size_t AsyncFullDuplexSocket::getRawBytesReceived() const { return 0; } + +void AsyncFullDuplexSocket::connect(ConnectCallback *callback, + const core::Prefix &prefix) { + connect_callback_ = callback; + + // Create an interest for a subscription + auto interest = + core::Interest::Ptr(new core::Interest(prefix.makeRandomName())); + auto _payload = utils::MemBuf::create(sizeof(ActionMessage)); + _payload->append(sizeof(ActionMessage)); + auto payload = _payload->writableData(); + ActionMessage *subscription_message = + reinterpret_cast(payload); + subscription_message->header.msg_type = MessageType::ACTION; + subscription_message->action = Action::SUBSCRIBE; + subscription_message->header.reserved[0] = 0; + subscription_message->header.reserved[1] = 0; + + // Set the name the other part should use for notifying a content production + sync_notification_ = std::move(locator_.makeRandomName()); + sync_notification_.copyToDestination( + reinterpret_cast(subscription_message->name)); + + TRANSPORT_LOGI( + "Trying to connect. Sending interest: %s, name for notifications: %s", + prefix.getName().toString().c_str(), + sync_notification_.toString().c_str()); + + interest->setLifetime(1000); + interest->appendPayload(std::move(_payload)); + consumer_->asyncSendInterest(std::move(interest), + internal_connect_callback_.get()); +} + +void AsyncFullDuplexSocket::write(WriteCallback *callback, const void *buf, + size_t bytes, + const PublicationOptions &options, + WriteFlags flags) { + using namespace transport; + + // 1 asynchronously write the content. I assume here the + // buffer contains the whole application frame. FIXME: check + // if this is true and fix it accordingly + std::cout << "Size of the PAYLOAD: " << bytes << std::endl; + + if (bytes > core::Packet::default_mtu - sizeof(PayloadMessage)) { + TRANSPORT_LOGI("Producing content with name %s", + options.name.toString().c_str()); + producer_->asyncProduce(options.name, + reinterpret_cast(buf), bytes); + signalProductionToSubscribers(options.name); + } else { + TRANSPORT_LOGI("Sending payload through interest"); + piggybackPayloadToSubscribers( + options.name, reinterpret_cast(buf), bytes); + } +} + +void AsyncFullDuplexSocket::write( + WriteCallback *callback, utils::SharableVector &&output_buffer, + const PublicationOptions &options, WriteFlags flags) { + using namespace transport; + + // 1 asynchronously write the content. I assume here the + // buffer contains the whole application frame. FIXME: check + // if this is true and fix it accordingly + std::cout << "Size of the PAYLOAD: " << output_buffer.size() << std::endl; + + if (output_buffer.size() > + core::Packet::default_mtu - sizeof(PayloadMessage)) { + TRANSPORT_LOGI("Producing content with name %s", + options.name.toString().c_str()); + producer_->asyncProduce(options.name, std::move(output_buffer)); + signalProductionToSubscribers(options.name); + } else { + TRANSPORT_LOGI("Sending payload through interest"); + piggybackPayloadToSubscribers(options.name, &output_buffer[0], + output_buffer.size()); + } +} + +void AsyncFullDuplexSocket::piggybackPayloadToSubscribers( + const core::Name &name, const uint8_t *buffer, std::size_t bytes) { + for (auto &sub : subscribers_) { + auto interest = core::Interest::Ptr(new core::Interest(name)); + auto _payload = utils::MemBuf::create(bytes + sizeof(PayloadMessage)); + _payload->append(bytes + sizeof(PayloadMessage)); + auto payload = _payload->writableData(); + + PayloadMessage *interest_payload = + reinterpret_cast(payload); + interest_payload->header.msg_type = MessageType::PAYLOAD; + interest_payload->header.reserved[0] = 0; + interest_payload->header.reserved[1] = 0; + interest_payload->reserved[0] = 0; + std::memcpy(payload + sizeof(PayloadMessage), buffer, bytes); + interest->appendPayload(std::move(_payload)); + + // Set the timeout of 0.2 second + interest->setLifetime(1000); + interest->setName(sub); + interest->getWritableName().setSuffix(incremental_suffix_++); + // TRANSPORT_LOGI("Sending signalization to %s", + // interest->getName().toString().c_str()); + + consumer_->asyncSendInterest(std::move(interest), + internal_signal_callback_.get()); + } +} + +void AsyncFullDuplexSocket::signalProductionToSubscribers( + const core::Name &name) { + // Signal the other part we are producing a content + // Create an interest for a subscription + + for (auto &sub : subscribers_) { + auto interest = core::Interest::Ptr(new core::Interest(name)); + // Todo consider using preallocated pool of membufs + auto _payload = utils::MemBuf::create(sizeof(ActionMessage)); + _payload->append(sizeof(ActionMessage)); + auto payload = const_cast(interest->getPayload().data()); + + ActionMessage *produce_notification = + reinterpret_cast(payload); + produce_notification->header.msg_type = MessageType::ACTION; + produce_notification->action = Action::SIGNAL_PRODUCTION; + produce_notification->header.reserved[0] = 0; + produce_notification->header.reserved[1] = 0; + name.copyToDestination( + reinterpret_cast(produce_notification->name)); + interest->appendPayload(std::move(_payload)); + + // Set the timeout of 0.2 second + interest->setLifetime(1000); + interest->setName(sub); + interest->getWritableName().setSuffix(incremental_suffix_++); + // TRANSPORT_LOGI("Sending signalization to %s", + // interest->getName().toString().c_str()); + + consumer_->asyncSendInterest(std::move(interest), + internal_signal_callback_.get()); + } +} + +void AsyncFullDuplexSocket::waitForSubscribers(AcceptCallback *cb) { + accept_callback_ = cb; +} + +std::shared_ptr +AsyncFullDuplexSocket::decodeSynchronizationMessage( + const core::Interest &interest) { + auto mesg = interest.getPayload(); + const MessageHeader *header = + reinterpret_cast(mesg.data()); + + switch (header->msg_type) { + case MessageType::ACTION: { + // Check what is the action to perform + const ActionMessage *message = + reinterpret_cast(header); + + if (message->action == Action::SUBSCRIBE) { + // Add consumer to list on consumers to be notified + auto ret = + subscribers_.emplace(AF_INET6, (const uint8_t *)message->name, 0); + TRANSPORT_LOGI("Added subscriber %s :)", ret.first->toString().c_str()); + if (ret.second) { + accept_callback_->connectionAccepted(*ret.first); + } + + TRANSPORT_LOGI("Connection success!"); + + sync_notification_ = std::move(locator_.makeRandomName()); + return createSubscriptionResponse(sync_notification_); + + } else if (message->action == Action::CANCEL_SUBSCRIPTION) { + // XXX Modify name!!! Each allocated name allocates a 128 bit array. + subscribers_.erase( + core::Name(AF_INET6, (const uint8_t *)message->name, 0)); + return createAck(); + } else if (message->action == Action::SIGNAL_PRODUCTION) { + // trigger a reverse pull for the name contained in the message + core::Name n(AF_INET6, (const uint8_t *)message->name, 0); + std::cout << "PROD NOTIFICATION: Content to retrieve: " << n + << std::endl; + std::cout << "PROD NOTIFICATION: Interest name: " << interest.getName() + << std::endl; // << " compared to " << sync_notification_ << + // std::endl; + + if (sync_notification_.equals(interest.getName(), false)) { + std::cout << "Starting reverse pull for " << n << std::endl; + consumer_->asyncConsume(n, receive_buffer_); + return createAck(); + } + } else { + TRANSPORT_LOGE("Received unknown message. Dropping it."); + } + + break; + } + case MessageType::RESPONSE: { + throw errors::RuntimeException( + "The response should be a content object!!"); + } + case MessageType::PAYLOAD: { + // The interest contains the payload directly. + // We saved one round trip :) + + auto buffer = std::make_shared>(); + const uint8_t *data = mesg.data() + sizeof(PayloadMessage); + buffer->assign(data, data + mesg.length() - sizeof(PayloadMessage)); + read_callback_->readBufferAvailable(std::move(*buffer)); + return createAck(); + } + default: { + return std::shared_ptr(nullptr); + } + } + + return std::shared_ptr(nullptr); +} + +void AsyncFullDuplexSocket::onControlInterest(ProducerSocket &s, + const core::Interest &i) { + auto payload = i.getPayload(); + if (payload.length()) { + // Try to decode payload and see if starting an async pull operation + auto response = decodeSynchronizationMessage(i); + if (response) { + response->setName(i.getName()); + s.produce(*response); + } + } +} + +void AsyncFullDuplexSocket::onContentProduced(ProducerSocket &producer, + const std::error_code &ec, + uint64_t bytes_written) { + if (write_callback_) { + if (!ec) { + write_callback_->writeSuccess(); + } else { + write_callback_->writeErr(bytes_written); + } + } +} + +void AsyncFullDuplexSocket::onContentRetrieved(ConsumerSocket &s, + std::size_t size, + const std::error_code &ec) { + // Sanity check + if (size != receive_buffer_->size()) { + TRANSPORT_LOGE( + "Received content size differs from size retrieved from the buffer."); + return; + } + + TRANSPORT_LOGI("Received content with size %lu", size); + if (!ec) { + read_callback_->readBufferAvailable(std::move(*receive_buffer_)); + } else { + TRANSPORT_LOGE("Error retrieving content."); + } + // consumer_->stop(); +} + +void AsyncFullDuplexSocket::OnConnectCallback::onContentObject( + core::Interest::Ptr &&, core::ContentObject::Ptr &&content_object) { + // The ack message should contain the name to be used for notifying + // the production of the content to the other part + + if (content_object->getPayload().length() == 0) { + TRANSPORT_LOGW("Connection response message empty...."); + return; + } + + SubscriptionResponseMessage *response = + reinterpret_cast( + content_object->getPayload().writableData()); + + if (response->response.header.msg_type == MessageType::RESPONSE) { + if (response->response.return_code == ReturnCode::OK) { + auto ret = + socket_.subscribers_.emplace(AF_INET6, (uint8_t *)response->name, 0); + TRANSPORT_LOGI("Successfully connected!!!! Subscriber added: %s", + ret.first->toString().c_str()); + socket_.connect_callback_->connectSuccess(); + } + } +} + +void AsyncFullDuplexSocket::OnSignalCallback::onContentObject( + core::Interest::Ptr &&, core::ContentObject::Ptr &&content_object) { + return; +} + +void AsyncFullDuplexSocket::OnSignalCallback::onTimeout( + core::Interest::Ptr &&interest) { + TRANSPORT_LOGE("Retransmitting signalization interest to %s!!", + interest->getName().toString().c_str()); + socket_.consumer_->asyncSendInterest(std::move(interest), + socket_.internal_signal_callback_.get()); +} + +void AsyncFullDuplexSocket::OnConnectCallback::onTimeout( + core::Interest::Ptr &&interest) { + socket_.connect_callback_->connectErr( + std::make_error_code(std::errc::not_connected)); +} + +std::shared_ptr AsyncFullDuplexSocket::createAck() { + // Send the response back + core::Name name("b001::abcd"); + auto response = std::make_shared(name); + auto _payload = utils::MemBuf::create(sizeof(ActionMessage)); + _payload->append(sizeof(ResponseMessage)); + auto payload = response->getPayload().data(); + ResponseMessage *response_message = (ResponseMessage *)payload; + response_message->header.msg_type = MessageType::RESPONSE; + response_message->header.reserved[0] = 0; + response_message->header.reserved[1] = 0; + response_message->return_code = ReturnCode::OK; + response->appendPayload(std::move(_payload)); + response->setLifetime(0); + return response; +} + +std::shared_ptr +AsyncFullDuplexSocket::createSubscriptionResponse(const core::Name &name) { + // Send the response back + core::Name tmp_name("b001::abcd"); + auto response = std::make_shared(tmp_name); + auto _payload = utils::MemBuf::create(sizeof(SubscriptionResponseMessage)); + _payload->append(sizeof(SubscriptionResponseMessage)); + auto payload = _payload->data(); + SubscriptionResponseMessage *response_message = + (SubscriptionResponseMessage *)payload; + response_message->response.header.msg_type = MessageType::RESPONSE; + response_message->response.header.reserved[0] = 0; + response_message->response.header.reserved[1] = 0; + response_message->response.return_code = ReturnCode::OK; + name.copyToDestination(reinterpret_cast(response_message->name)); + response->appendPayload(std::move(_payload)); + response->setLifetime(0); + return response; +} + +} // namespace interface +} // namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/full_duplex_socket.h b/libtransport/src/hicn/transport/interfaces/full_duplex_socket.h new file mode 100755 index 000000000..f881bea54 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/full_duplex_socket.h @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * This class is created for sending/receiving data over an ICN network. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace transport { + +namespace interface { + +enum class MessageType : uint8_t { ACTION, RESPONSE, PAYLOAD }; + +enum class Action : uint8_t { + SUBSCRIBE, + CANCEL_SUBSCRIPTION, + SIGNAL_PRODUCTION, +}; + +enum class ReturnCode : uint8_t { + OK, + FAILED, +}; + +struct MessageHeader { + MessageType msg_type; + uint8_t reserved[2]; +}; + +struct ActionMessage { + MessageHeader header; + Action action; + uint64_t name[2]; +}; + +struct ResponseMessage { + MessageHeader header; + ReturnCode return_code; +}; + +struct SubscriptionResponseMessage { + ResponseMessage response; + uint64_t name[2]; +}; + +struct PayloadMessage { + MessageHeader header; + uint8_t reserved[1]; +}; + +// struct NotificationMessage { +// Action action; +// uint8_t reserved[3]; +// uint64_t +// } + +using core::Prefix; + +class AsyncFullDuplexSocket : public AsyncSocket, + public AsyncReader, + public AsyncWriter, + public AsyncAcceptor { + private: + struct Counters { + uint64_t app_bytes_written_; + uint64_t app_bytes_read_; + + TRANSPORT_ALWAYS_INLINE void updateBytesWritten(uint64_t bytes) { + app_bytes_written_ += bytes; + } + + TRANSPORT_ALWAYS_INLINE void updateBytesRead(uint64_t bytes) { + app_bytes_read_ += bytes; + } + }; + + public: + using UniquePtr = std::unique_ptr; + using SharedPtr = std::unique_ptr; + + AsyncFullDuplexSocket(const Prefix &locator, asio::io_service &io_service); + AsyncFullDuplexSocket(const core::Prefix &locator); + + ~AsyncFullDuplexSocket() { + TRANSPORT_LOGI("Adios AsyncFullDuplexSocket!!!"); + }; + + using ReadCallback = AsyncReader::ReadCallback; + using WriteCallback = AsyncWriter::WriteCallback; + + TRANSPORT_ALWAYS_INLINE void setReadCB(ReadCallback *callback) override { + read_callback_ = callback; + } + + TRANSPORT_ALWAYS_INLINE ReadCallback *getReadCallback() const override { + return read_callback_; + } + + TRANSPORT_ALWAYS_INLINE void setWriteCB(WriteCallback *callback) override { + write_callback_ = callback; + } + + TRANSPORT_ALWAYS_INLINE WriteCallback *getWriteCallback() const override { + return write_callback_; + } + + TRANSPORT_ALWAYS_INLINE const core::Prefix &getLocator() { return locator_; } + + void connect(ConnectCallback *callback, const core::Prefix &prefix) override; + + void write(WriteCallback *callback, const void *buf, size_t bytes, + const PublicationOptions &options, + WriteFlags flags = WriteFlags::NONE) override; + + virtual void write(WriteCallback *callback, + utils::SharableVector &&output_buffer, + const PublicationOptions &options, + WriteFlags flags = WriteFlags::NONE) override; + + void waitForSubscribers(AcceptCallback *cb) override; + + // void writev( + // WriteCallback* callback, + // const iovec* vec, + // size_t count, + // Name &&content_to_publish_name, + // WriteFlags flags = WriteFlags::NONE) override; + + void close() override; + + void closeNow() override; + + void shutdownWrite() override; + + void shutdownWriteNow() override; + + bool good() const override; + + bool readable() const override; + + bool writable() const override; + + bool isPending() const override; + + bool connected() const override; + + bool error() const override; + + void setSendTimeout(uint32_t milliseconds) override; + + size_t getAppBytesWritten() const override; + size_t getRawBytesWritten() const override; + size_t getAppBytesReceived() const override; + size_t getRawBytesReceived() const override; + + uint32_t getSendTimeout() const override; + + private: + std::shared_ptr decodeSynchronizationMessage( + const core::Interest &interest); + + class OnConnectCallback : public BasePortal::ConsumerCallback { + public: + OnConnectCallback(AsyncFullDuplexSocket &socket) : socket_(socket){}; + virtual ~OnConnectCallback() = default; + void onContentObject(core::Interest::Ptr &&, + core::ContentObject::Ptr &&content_object) override; + void onTimeout(core::Interest::Ptr &&interest) override; + + private: + AsyncFullDuplexSocket &socket_; + }; + + class OnSignalCallback : public BasePortal::ConsumerCallback { + public: + OnSignalCallback(AsyncFullDuplexSocket &socket) : socket_(socket){}; + virtual ~OnSignalCallback() = default; + void onContentObject(core::Interest::Ptr &&, + core::ContentObject::Ptr &&content_object); + void onTimeout(core::Interest::Ptr &&interest); + + private: + AsyncFullDuplexSocket &socket_; + }; + + void onControlInterest(ProducerSocket &s, const core::Interest &i); + void onContentProduced(ProducerSocket &producer, const std::error_code &ec, + uint64_t bytes_written); + void onContentRetrieved(ConsumerSocket &s, std::size_t size, + const std::error_code &ec); + + void signalProductionToSubscribers(const core::Name &name); + void piggybackPayloadToSubscribers(const core::Name &name, + const uint8_t *buffer, std::size_t bytes); + + std::shared_ptr createAck(); + std::shared_ptr createSubscriptionResponse( + const core::Name &name); + + core::Prefix locator_; + uint32_t incremental_suffix_; + core::Name sync_notification_; + // std::unique_ptr portal_; + asio::io_service internal_io_service_; + asio::io_service &io_service_; + asio::io_service::work work_; + + // These names represent the "locator" of a certain + // peer that subscribed to this. + std::unordered_set subscribers_; + + // Useful for publishing / Retrieving data + std::unique_ptr producer_; + std::unique_ptr consumer_; + + ReadCallback *read_callback_; + WriteCallback *write_callback_; + ConnectCallback *connect_callback_; + AcceptCallback *accept_callback_; + + std::unique_ptr internal_connect_callback_; + std::unique_ptr internal_signal_callback_; + + uint32_t send_timeout_milliseconds_; + struct Counters counters_; + std::shared_ptr> receive_buffer_; +}; + +} // namespace interface +} // namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/publication_options.h b/libtransport/src/hicn/transport/interfaces/publication_options.h new file mode 100755 index 000000000..ae5366ce7 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/publication_options.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +namespace transport { + +namespace interface { + +class PublicationOptions { + public: + core::Name name; + uint32_t content_lifetime_milliseconds; + // TODO Signature +}; +} // namespace interface + +} // namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/interfaces/rtc_socket_consumer.cc b/libtransport/src/hicn/transport/interfaces/rtc_socket_consumer.cc new file mode 100755 index 000000000..de3e84417 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/rtc_socket_consumer.cc @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +namespace transport { + +namespace interface { + +RTCConsumerSocket::RTCConsumerSocket(int protocol, asio::io_service &io_service) + : ConsumerSocket(protocol, io_service) {} + +RTCConsumerSocket::~RTCConsumerSocket() {} + +void RTCConsumerSocket::handleRTCPPacket(uint8_t *packet, size_t len) { + RTCTransportProtocol *transport = dynamic_cast( + ConsumerSocket::transport_protocol_.get()); + if (transport) transport->onRTCPPacket(packet, len); +} + +} // namespace interface + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/rtc_socket_consumer.h b/libtransport/src/hicn/transport/interfaces/rtc_socket_consumer.h new file mode 100755 index 000000000..86ccf6e22 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/rtc_socket_consumer.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace transport { + +namespace interface { + +class RTCConsumerSocket : public ConsumerSocket { + public: + explicit RTCConsumerSocket(int protocol, asio::io_service &io_service); + + ~RTCConsumerSocket(); + + void handleRTCPPacket(uint8_t *packet, size_t len); +}; + +} // namespace interface + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/rtc_socket_producer.cc b/libtransport/src/hicn/transport/interfaces/rtc_socket_producer.cc new file mode 100755 index 000000000..d8a9d53b9 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/rtc_socket_producer.cc @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#define NACK_HEADER_SIZE 8 // bytes +#define TIMESTAMP_LEN 8 // bytes +#define TCP_HEADER_SIZE 20 +#define IP6_HEADER_SIZE 40 +#define INIT_PACKET_PRODUCTION_RATE 100 // pps random value (almost 1Mbps) +#define STATS_INTERVAL_DURATION 500 // ms +#define INTEREST_LIFETIME_REDUCTION_FACTOR 0.8 + +// NACK HEADER +// +-----------------------------------------+ +// | 4 bytes: current segment in production | +// +-----------------------------------------+ +// | 4 bytes: production rate (bytes x sec) | +// +-----------------------------------------+ +// may require additional field (Rate for multiple qualities, ...) +// + +namespace transport { + +namespace interface { + +RTCProducerSocket::RTCProducerSocket(asio::io_service &io_service) + : ProducerSocket(io_service), + currentSeg_(1), + nack_(std::make_shared()), + producedBytes_(0), + producedPackets_(0), + bytesProductionRate_(0), + packetsProductionRate_(INIT_PACKET_PRODUCTION_RATE), + perSecondFactor_(1000 / STATS_INTERVAL_DURATION) { + nack_->appendPayload(utils::MemBuf::create(NACK_HEADER_SIZE)); + lastStats_ = std::chrono::steady_clock::now(); + srand(time(NULL)); + prodLabel_ = ((rand() % 255) << 24UL); +} + +RTCProducerSocket::~RTCProducerSocket() {} + +void RTCProducerSocket::registerName(Prefix &producer_namespace) { + ProducerSocket::registerPrefix(producer_namespace); + + flowName_ = producer_namespace.getName(); + + if (flowName_.getType() == HNT_CONTIGUOUS_V4 || + flowName_.getType() == HNT_IOV_V4) { + headerSize_ = sizeof(hicn_v6_hdr_t::ip); + } else if (flowName_.getType() == HNT_CONTIGUOUS_V6 || + flowName_.getType() == HNT_IOV_V6) { + headerSize_ = sizeof(hicn_v4_hdr_t::ip); + } else { + throw errors::RuntimeException("Unknown name format."); + } + + headerSize_ += TCP_HEADER_SIZE; +} + +void RTCProducerSocket::updateStats(uint32_t packet_size) { + producedBytes_ += packet_size; + producedPackets_++; + std::chrono::steady_clock::duration duration = + std::chrono::steady_clock::now() - lastStats_; + if (std::chrono::duration_cast(duration).count() >= + STATS_INTERVAL_DURATION) { + lastStats_ = std::chrono::steady_clock::now(); + bytesProductionRate_ = producedBytes_ * perSecondFactor_; + packetsProductionRate_ = producedPackets_ * perSecondFactor_; + producedBytes_ = 0; + producedPackets_ = 0; + } +} + +void RTCProducerSocket::produce(const uint8_t *buf, size_t buffer_size) { + if (TRANSPORT_EXPECT_FALSE(buffer_size == 0)) { + return; + } + + if (TRANSPORT_EXPECT_FALSE((buffer_size + headerSize_ + TIMESTAMP_LEN) > + data_packet_size_)) { + return; + } + + updateStats(buffer_size + headerSize_ + TIMESTAMP_LEN); + + std::shared_ptr content_object = + std::make_shared(flowName_.setSuffix(currentSeg_)); + auto payload = utils::MemBuf::copyBuffer(buf, buffer_size, TIMESTAMP_LEN); + + // content_object->setLifetime(content_object_expiry_time_); + content_object->setLifetime(1000); // XXX this should be set by the APP + + uint64_t timestamp = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + + payload->prepend(TIMESTAMP_LEN); + uint8_t *payloadPointer = payload->writableData(); + *(uint64_t *)payloadPointer = timestamp; + content_object->appendPayload(std::move(payload)); + + content_object->setPathLabel(prodLabel_); + portal_->sendContentObject(*content_object); + + currentSeg_++; +} + +void RTCProducerSocket::onInterest(Interest::Ptr &&interest) { + uint32_t interestSeg = interest->getName().getSuffix(); + uint32_t lifetime = interest->getLifetime(); + uint32_t max_gap; + + // XXX + // packetsProductionRate_ is modified by another thread in updateStats + // this should be safe since I just read here. but, you never know. + max_gap = + floor((double)((double)((double)lifetime * + INTEREST_LIFETIME_REDUCTION_FACTOR / 1000.0) * + (double)packetsProductionRate_)); + + if (interestSeg < currentSeg_ || interestSeg > (max_gap + currentSeg_)) { + sendNack(*interest); + } + // else drop packet +} + +void RTCProducerSocket::sendNack(const Interest &interest) { + nack_->setName(interest.getName()); + uint32_t *payload_ptr = (uint32_t *)nack_->getPayload().data(); + *payload_ptr = currentSeg_; + *(++payload_ptr) = bytesProductionRate_; + + nack_->setLifetime(0); + nack_->setPathLabel(prodLabel_); + portal_->sendContentObject(*nack_); +} + +} // namespace interface + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/rtc_socket_producer.h b/libtransport/src/hicn/transport/interfaces/rtc_socket_producer.h new file mode 100755 index 000000000..1a42bdc56 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/rtc_socket_producer.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include +#include + +namespace transport { + +namespace interface { + +class RTCProducerSocket : public ProducerSocket { + public: + RTCProducerSocket(asio::io_service &io_service); + ~RTCProducerSocket(); + + void registerName(Prefix &producer_namespace); + + void produce(const uint8_t *buffer, size_t buffer_size); + + void onInterest(Interest::Ptr &&interest) override; + + private: + void sendNack(const Interest &interest); + void updateStats(uint32_t packet_size); + + // std::map pendingInterests_; + uint32_t currentSeg_; + uint32_t prodLabel_; + uint16_t headerSize_; + Name flowName_; + // bool produceInSynch_; + std::shared_ptr nack_; + uint32_t producedBytes_; + uint32_t producedPackets_; + uint32_t bytesProductionRate_; + uint32_t packetsProductionRate_; + uint32_t perSecondFactor_; + std::chrono::steady_clock::time_point lastStats_; +}; + +} // namespace interface + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/socket.h b/libtransport/src/hicn/transport/interfaces/socket.h new file mode 100755 index 000000000..22757810a --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/socket.h @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SOCKET_OPTION_GET 0 +#define SOCKET_OPTION_NOT_GET 1 +#define SOCKET_OPTION_SET 2 +#define SOCKET_OPTION_NOT_SET 3 +#define SOCKET_OPTION_DEFAULT 12345 + +#define VOID_HANDLER 0 + +namespace transport { + +namespace protocol { +class IcnObserver; +} + +namespace interface { + +template +class Socket; +class ConsumerSocket; +class ProducerSocket; + +// using Interest = core::Interest; +// using ContentObject = core::ContentObject; +// using Name = core::Name; +// using HashAlgorithm = core::HashAlgorithm; +// using CryptoSuite = utils::CryptoSuite; +// using Identity = utils::Identity; +// using Verifier = utils::Verifier; + +using HicnForwarderPortal = core::HicnForwarderPortal; + +#ifdef __linux__ +#ifndef __ANDROID__ +using RawSocketPortal = core::RawSocketPortal; +#endif +#endif + +#ifdef __vpp__ +using VPPForwarderPortal = core::VPPForwarderPortal; +using BaseSocket = Socket; +using BasePortal = VPPForwarderPortal; +#else +using BaseSocket = Socket; +using BasePortal = HicnForwarderPortal; +#endif + +using PayloadType = core::PayloadType; +using Prefix = core::Prefix; +using Array = utils::Array; + +using ConsumerInterestCallback = + std::function; + +using ConsumerContentCallback = + std::function; + +using ConsumerTimerCallback = + std::function; + +using ProducerContentCallback = std::function; + +using ConsumerContentObjectCallback = + std::function; + +using ConsumerContentObjectVerificationCallback = + std::function; + +using ConsumerManifestCallback = + std::function; + +using ProducerContentObjectCallback = + std::function; + +using ProducerInterestCallback = + std::function; + +using ProducerInterestCallback = + std::function; + +using namespace protocol; + +template +class Socket { + static_assert(std::is_same::value +#ifdef __linux__ +#ifndef __ANDROID__ + || std::is_same::value +#ifdef __vpp__ + || std::is_same::value +#endif +#endif + , +#else + , + +#endif + "This class is not allowed as Portal"); + + public: + using Portal = PortalType; + + virtual asio::io_service &getIoService() = 0; + + virtual void connect() = 0; + + virtual int setSocketOption(int socket_option_key, + uint32_t socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + double socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + bool socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + core::Name socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + std::list socket_option_value) = 0; + + virtual int setSocketOption( + int socket_option_key, + ProducerContentObjectCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ProducerInterestCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ProducerContentCallback socket_option_value) = 0; + + virtual int setSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback socket_option_value) = 0; + + virtual int setSocketOption( + int socket_option_key, + ConsumerContentObjectCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ConsumerInterestCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ConsumerContentCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ConsumerManifestCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + IcnObserver *socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + core::HashAlgorithm socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + utils::CryptoSuite socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + const utils::Identity &socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ConsumerTimerCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + const std::string &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + uint32_t &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + double &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + bool &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + core::Name &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + std::list &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, + ProducerContentObjectCallback &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, ProducerInterestCallback &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, + ConsumerContentObjectCallback &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, ConsumerInterestCallback &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + ConsumerContentCallback &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, ConsumerManifestCallback &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + ProducerContentCallback &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + std::shared_ptr &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + IcnObserver **socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + core::HashAlgorithm &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + utils::CryptoSuite &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + utils::Identity &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + std::string &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + ConsumerTimerCallback &socket_option_value) = 0; + + protected: + virtual ~Socket(){}; + + protected: + std::string output_interface_; +}; + +} // namespace interface + +} // namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/socket_consumer.cc b/libtransport/src/hicn/transport/interfaces/socket_consumer.cc new file mode 100755 index 000000000..8109d0e99 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/socket_consumer.cc @@ -0,0 +1,735 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +namespace transport { + +namespace interface { + +ConsumerSocket::ConsumerSocket(int protocol) + : ConsumerSocket(protocol, internal_io_service_) {} + +ConsumerSocket::ConsumerSocket(int protocol, asio::io_service &io_service) + : io_service_(io_service), + portal_(std::make_shared(io_service_)), + async_downloader_(), + interest_lifetime_(default_values::interest_lifetime), + min_window_size_(default_values::min_window_size), + max_window_size_(default_values::max_window_size), + current_window_size_(-1), + max_retransmissions_( + default_values::transport_protocol_max_retransmissions), + /****** RAAQM Parameters ******/ + minimum_drop_probability_(default_values::minimum_drop_probability), + sample_number_(default_values::sample_number), + gamma_(default_values::gamma_value), + beta_(default_values::beta_value), + drop_factor_(default_values::drop_factor), + /****** END RAAQM Parameters ******/ + rate_estimation_alpha_(default_values::rate_alpha), + rate_estimation_observer_(nullptr), + rate_estimation_choice_(0), + is_async_(false), + verify_signature_(false), + content_buffer_(nullptr), + on_interest_output_(VOID_HANDLER), + on_interest_timeout_(VOID_HANDLER), + on_interest_satisfied_(VOID_HANDLER), + on_content_object_input_(VOID_HANDLER), + on_content_object_verification_(VOID_HANDLER), + on_content_object_(VOID_HANDLER), + on_manifest_(VOID_HANDLER), + on_payload_retrieved_(VOID_HANDLER), + virtual_download_(false), + rtt_stats_(false), + timer_(portal_->getIoService()), + timer_interval_milliseconds_(0) { + switch (protocol) { + case TransportProtocolAlgorithms::VEGAS: + transport_protocol_ = std::make_shared(this); + break; + case TransportProtocolAlgorithms::CBR: + transport_protocol_ = std::make_shared(this); + break; + case TransportProtocolAlgorithms::RTC: + transport_protocol_ = std::make_shared(this); + break; + case TransportProtocolAlgorithms::RAAQM: + default: + transport_protocol_ = std::make_shared(this); + break; + } +} + +ConsumerSocket::~ConsumerSocket() { + stop(); + + async_downloader_.stop(); + + transport_protocol_.reset(); + portal_.reset(); +} + +void ConsumerSocket::connect() { portal_->connect(); } + +int ConsumerSocket::consume(const Name &name, + utils::SharableVector &receive_buffer) { + if (transport_protocol_->isRunning()) { + return CONSUMER_BUSY; + } + + content_buffer_ = receive_buffer.shared_from_this(); + + network_name_ = name; + network_name_.setSuffix(0); + is_async_ = false; + + transport_protocol_->start(receive_buffer); + + return CONSUMER_READY; +} + +int ConsumerSocket::asyncConsume( + const Name &name, + std::shared_ptr> receive_buffer) { + // XXX Try to move the name here, instead of copying it!! + if (!async_downloader_.stopped()) { + async_downloader_.add([this, receive_buffer, name]() { + network_name_ = std::move(name); + network_name_.setSuffix(0); + is_async_ = true; + transport_protocol_->start(*receive_buffer); + }); + } + + return CONSUMER_READY; +} + +void ConsumerSocket::asyncSendInterest(Interest::Ptr &&interest, + Portal::ConsumerCallback *callback) { + if (!async_downloader_.stopped()) { + // TODO Workaround, to be fixed! + auto i = interest.release(); + async_downloader_.add([this, i, callback]() mutable { + Interest::Ptr _interest(i); + portal_->setConsumerCallback(callback); + portal_->sendInterest(std::move(_interest)); + portal_->runEventsLoop(); + }); + } +} + +void ConsumerSocket::stop() { + if (transport_protocol_->isRunning()) { + transport_protocol_->stop(); + } + + //is_running_ = false; +} + +void ConsumerSocket::resume() { + if(!transport_protocol_->isRunning()){ + transport_protocol_->resume(); + } +} + +asio::io_service &ConsumerSocket::getIoService() { + return portal_->getIoService(); +} + +int ConsumerSocket::setSocketOption(int socket_option_key, + double socket_option_value) { + switch (socket_option_key) { + case MIN_WINDOW_SIZE: + min_window_size_ = socket_option_value; + return SOCKET_OPTION_SET; + + case MAX_WINDOW_SIZE: + max_window_size_ = socket_option_value; + return SOCKET_OPTION_SET; + + case CURRENT_WINDOW_SIZE: + current_window_size_ = socket_option_value; + return SOCKET_OPTION_SET; + + case GAMMA_VALUE: + gamma_ = socket_option_value; + return SOCKET_OPTION_SET; + + case BETA_VALUE: + beta_ = socket_option_value; + return SOCKET_OPTION_SET; + + case DROP_FACTOR: + drop_factor_ = socket_option_value; + return SOCKET_OPTION_SET; + + case MINIMUM_DROP_PROBABILITY: + minimum_drop_probability_ = socket_option_value; + return SOCKET_OPTION_SET; + + case RATE_ESTIMATION_ALPHA: + if (socket_option_value >= 0 && socket_option_value < 1) { + rate_estimation_alpha_ = socket_option_value; + } else { + rate_estimation_alpha_ = ALPHA; + } + return SOCKET_OPTION_SET; + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ConsumerSocket::setSocketOption(int socket_option_key, + uint32_t socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::INPUT_BUFFER_SIZE: + input_buffer_size_ = socket_option_value; + return SOCKET_OPTION_SET; + + case GeneralTransportOptions::OUTPUT_BUFFER_SIZE: + output_buffer_size_ = socket_option_value; + return SOCKET_OPTION_SET; + + case GeneralTransportOptions::MAX_INTEREST_RETX: + max_retransmissions_ = socket_option_value; + return SOCKET_OPTION_SET; + + case GeneralTransportOptions::INTEREST_LIFETIME: + interest_lifetime_ = socket_option_value; + return SOCKET_OPTION_SET; + + case ConsumerCallbacksOptions::INTEREST_RETRANSMISSION: + if (socket_option_value == VOID_HANDLER) { + on_interest_retransmission_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ConsumerCallbacksOptions::INTEREST_EXPIRED: + if (socket_option_value == VOID_HANDLER) { + on_interest_timeout_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ConsumerCallbacksOptions::INTEREST_SATISFIED: + if (socket_option_value == VOID_HANDLER) { + on_interest_satisfied_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ConsumerCallbacksOptions::INTEREST_OUTPUT: + if (socket_option_value == VOID_HANDLER) { + on_interest_output_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ConsumerCallbacksOptions::CONTENT_OBJECT_INPUT: + if (socket_option_value == VOID_HANDLER) { + on_content_object_input_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ConsumerCallbacksOptions::CONTENT_OBJECT_TO_VERIFY: + if (socket_option_value == VOID_HANDLER) { + on_content_object_verification_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ConsumerCallbacksOptions::CONTENT_RETRIEVED: + if (socket_option_value == VOID_HANDLER) { + on_payload_retrieved_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case RateEstimationOptions::RATE_ESTIMATION_BATCH_PARAMETER: + if (socket_option_value > 0) { + rate_estimation_batching_parameter_ = socket_option_value; + } else { + rate_estimation_batching_parameter_ = BATCH; + } + return SOCKET_OPTION_SET; + + case RateEstimationOptions::RATE_ESTIMATION_CHOICE: + if (socket_option_value > 0) { + rate_estimation_choice_ = socket_option_value; + } else { + rate_estimation_choice_ = RATE_CHOICE; + } + return SOCKET_OPTION_SET; + + case GeneralTransportOptions::TIMER_INTERVAL: + timer_interval_milliseconds_ = socket_option_value; + TRANSPORT_LOGD("Ok set %d", timer_interval_milliseconds_); + return SOCKET_OPTION_SET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ConsumerSocket::setSocketOption(int socket_option_key, + bool socket_option_value) { + switch (socket_option_key) { + case OtherOptions::VIRTUAL_DOWNLOAD: + virtual_download_ = socket_option_value; + return SOCKET_OPTION_SET; + + case RaaqmTransportOptions::RTT_STATS: + rtt_stats_ = socket_option_value; + return SOCKET_OPTION_SET; + + case GeneralTransportOptions::VERIFY_SIGNATURE: + verify_signature_ = socket_option_value; + return SOCKET_OPTION_SET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ConsumerSocket::setSocketOption(int socket_option_key, + Name socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::NETWORK_NAME: + network_name_ = socket_option_value; + return SOCKET_OPTION_SET; + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ConsumerSocket::setSocketOption(int socket_option_key, + std::list socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ConsumerSocket::setSocketOption( + int socket_option_key, ConsumerContentObjectCallback socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::CONTENT_OBJECT_INPUT: + on_content_object_input_ = socket_option_value; + ; + return SOCKET_OPTION_SET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ConsumerSocket::setSocketOption( + int socket_option_key, ProducerContentObjectCallback socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ConsumerSocket::setSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::CONTENT_OBJECT_TO_VERIFY: + on_content_object_verification_ = socket_option_value; + return SOCKET_OPTION_SET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ConsumerSocket::setSocketOption( + int socket_option_key, ConsumerInterestCallback socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::INTEREST_RETRANSMISSION: + on_interest_retransmission_ = socket_option_value; + return SOCKET_OPTION_SET; + + case ConsumerCallbacksOptions::INTEREST_OUTPUT: + on_interest_output_ = socket_option_value; + return SOCKET_OPTION_SET; + + case ConsumerCallbacksOptions::INTEREST_EXPIRED: + on_interest_timeout_ = socket_option_value; + return SOCKET_OPTION_SET; + + case ConsumerCallbacksOptions::INTEREST_SATISFIED: + on_interest_satisfied_ = socket_option_value; + return SOCKET_OPTION_SET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ConsumerSocket::setSocketOption( + int socket_option_key, ProducerInterestCallback socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ConsumerSocket::setSocketOption( + int socket_option_key, ConsumerContentCallback socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::CONTENT_RETRIEVED: + on_payload_retrieved_ = socket_option_value; + return SOCKET_OPTION_SET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ConsumerSocket::setSocketOption( + int socket_option_key, ConsumerManifestCallback socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::MANIFEST_INPUT: + on_manifest_ = socket_option_value; + return SOCKET_OPTION_SET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ConsumerSocket::setSocketOption( + int socket_option_key, ProducerContentCallback socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ConsumerSocket::setSocketOption(int socket_option_key, + IcnObserver *socket_option_value) { + if (socket_option_key == RateEstimationOptions::RATE_ESTIMATION_OBSERVER) { + rate_estimation_observer_ = socket_option_value; + return SOCKET_OPTION_SET; + } + + return SOCKET_OPTION_NOT_SET; +} + +int ConsumerSocket::setSocketOption(int socket_option_key, + HashAlgorithm socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ConsumerSocket::setSocketOption(int socket_option_key, + utils::CryptoSuite socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ConsumerSocket::setSocketOption( + int socket_option_key, const utils::Identity &socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ConsumerSocket::setSocketOption(int socket_option_key, + const std::string &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::CERTIFICATE: + key_id_ = verifier_.addKeyFromCertificate(socket_option_value); + + if (key_id_ != nullptr) { + return SOCKET_OPTION_SET; + } + + break; + + case DataLinkOptions::OUTPUT_INTERFACE: + output_interface_ = socket_option_value; + portal_->setOutputInterface(output_interface_); + return SOCKET_OPTION_SET; + } + + return SOCKET_OPTION_NOT_SET; +} + +int ConsumerSocket::setSocketOption(int socket_option_key, + ConsumerTimerCallback socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::TIMER_EXPIRES: + on_timer_expires_ = socket_option_value; + return SOCKET_OPTION_SET; + } + + return SOCKET_OPTION_NOT_SET; +} + +int ConsumerSocket::getSocketOption(int socket_option_key, + double &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::MIN_WINDOW_SIZE: + socket_option_value = min_window_size_; + return SOCKET_OPTION_GET; + + case GeneralTransportOptions::MAX_WINDOW_SIZE: + socket_option_value = max_window_size_; + return SOCKET_OPTION_GET; + + case GeneralTransportOptions::CURRENT_WINDOW_SIZE: + socket_option_value = current_window_size_; + return SOCKET_OPTION_GET; + + // RAAQM parameters + + case RaaqmTransportOptions::GAMMA_VALUE: + socket_option_value = gamma_; + return SOCKET_OPTION_GET; + + case RaaqmTransportOptions::BETA_VALUE: + socket_option_value = beta_; + return SOCKET_OPTION_GET; + + case RaaqmTransportOptions::DROP_FACTOR: + socket_option_value = drop_factor_; + return SOCKET_OPTION_GET; + + case RaaqmTransportOptions::MINIMUM_DROP_PROBABILITY: + socket_option_value = minimum_drop_probability_; + return SOCKET_OPTION_GET; + + case RateEstimationOptions::RATE_ESTIMATION_ALPHA: + socket_option_value = rate_estimation_alpha_; + return SOCKET_OPTION_GET; + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ConsumerSocket::getSocketOption(int socket_option_key, + uint32_t &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::INPUT_BUFFER_SIZE: + socket_option_value = input_buffer_size_; + return SOCKET_OPTION_GET; + + case GeneralTransportOptions::OUTPUT_BUFFER_SIZE: + socket_option_value = output_buffer_size_; + return SOCKET_OPTION_GET; + + case GeneralTransportOptions::MAX_INTEREST_RETX: + socket_option_value = max_retransmissions_; + return SOCKET_OPTION_GET; + + case GeneralTransportOptions::INTEREST_LIFETIME: + socket_option_value = interest_lifetime_; + return SOCKET_OPTION_GET; + + case RaaqmTransportOptions::SAMPLE_NUMBER: + socket_option_value = sample_number_; + return SOCKET_OPTION_GET; + + case RateEstimationOptions::RATE_ESTIMATION_BATCH_PARAMETER: + socket_option_value = rate_estimation_batching_parameter_; + return SOCKET_OPTION_GET; + + case RateEstimationOptions::RATE_ESTIMATION_CHOICE: + socket_option_value = rate_estimation_choice_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ConsumerSocket::getSocketOption(int socket_option_key, + bool &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::ASYNC_MODE: + socket_option_value = is_async_; + return SOCKET_OPTION_GET; + + case GeneralTransportOptions::RUNNING: + socket_option_value = transport_protocol_->isRunning(); + return SOCKET_OPTION_GET; + + case OtherOptions::VIRTUAL_DOWNLOAD: + socket_option_value = virtual_download_; + return SOCKET_OPTION_GET; + + case RaaqmTransportOptions::RTT_STATS: + socket_option_value = rtt_stats_; + return SOCKET_OPTION_GET; + + case GeneralTransportOptions::VERIFY_SIGNATURE: + socket_option_value = verify_signature_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ConsumerSocket::getSocketOption(int socket_option_key, + Name &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::NETWORK_NAME: + socket_option_value = network_name_; + return SOCKET_OPTION_GET; + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ConsumerSocket::getSocketOption(int socket_option_key, + std::list &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ConsumerSocket::getSocketOption( + int socket_option_key, ConsumerContentObjectCallback &socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::CONTENT_OBJECT_INPUT: + socket_option_value = on_content_object_input_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ConsumerSocket::getSocketOption( + int socket_option_key, ProducerContentObjectCallback &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ConsumerSocket::getSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback &socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::CONTENT_OBJECT_TO_VERIFY: + socket_option_value = on_content_object_verification_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ConsumerSocket::getSocketOption( + int socket_option_key, ConsumerInterestCallback &socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::INTEREST_RETRANSMISSION: + socket_option_value = on_interest_retransmission_; + return SOCKET_OPTION_GET; + + case ConsumerCallbacksOptions::INTEREST_OUTPUT: + socket_option_value = on_interest_output_; + return SOCKET_OPTION_GET; + + case ConsumerCallbacksOptions::INTEREST_EXPIRED: + socket_option_value = on_interest_timeout_; + return SOCKET_OPTION_GET; + + case ConsumerCallbacksOptions::INTEREST_SATISFIED: + socket_option_value = on_interest_satisfied_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ConsumerSocket::getSocketOption( + int socket_option_key, ProducerInterestCallback &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ConsumerSocket::getSocketOption( + int socket_option_key, ConsumerContentCallback &socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::CONTENT_RETRIEVED: + socket_option_value = on_payload_retrieved_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ConsumerSocket::getSocketOption( + int socket_option_key, ConsumerManifestCallback &socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::MANIFEST_INPUT: + socket_option_value = on_manifest_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ConsumerSocket::getSocketOption( + int socket_option_key, std::shared_ptr &socket_option_value) { + switch (socket_option_key) { + case PORTAL: + socket_option_value = portal_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ConsumerSocket::getSocketOption(int socket_option_key, + IcnObserver **socket_option_value) { + if (socket_option_key == RATE_ESTIMATION_OBSERVER) { + *socket_option_value = (rate_estimation_observer_); + return SOCKET_OPTION_GET; + } + + return SOCKET_OPTION_NOT_GET; +} + +int ConsumerSocket::getSocketOption(int socket_option_key, + HashAlgorithm &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ConsumerSocket::getSocketOption(int socket_option_key, + utils::CryptoSuite &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ConsumerSocket::getSocketOption(int socket_option_key, + utils::Identity &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ConsumerSocket::getSocketOption( + int socket_option_key, ProducerContentCallback &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ConsumerSocket::getSocketOption(int socket_option_key, + std::string &socket_option_value) { + switch (socket_option_key) { + case DataLinkOptions::OUTPUT_INTERFACE: + socket_option_value = output_interface_; + return SOCKET_OPTION_GET; + } + + return SOCKET_OPTION_NOT_GET; +} + +int ConsumerSocket::getSocketOption( + int socket_option_key, ConsumerTimerCallback &socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::TIMER_EXPIRES: + socket_option_value = on_timer_expires_; + return SOCKET_OPTION_GET; + } + + return SOCKET_OPTION_NOT_GET; +} + +} // namespace interface + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/socket_consumer.h b/libtransport/src/hicn/transport/interfaces/socket_consumer.h new file mode 100755 index 000000000..9e309aae8 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/socket_consumer.h @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#define CONSUMER_READY 0 +#define CONSUMER_BUSY 1 + +namespace transport { + +namespace interface { + +class ConsumerSocket : public BaseSocket { + friend class protocol::TransportProtocol; + friend class protocol::VegasTransportProtocol; + friend class protocol::RaaqmTransportProtocol; + friend class protocol::CbrTransportProtocol; + + public: + explicit ConsumerSocket(int protocol); + explicit ConsumerSocket(int protocol, asio::io_service &io_service); + + ~ConsumerSocket(); + + void connect() override; + + int consume(const Name &name, utils::SharableVector &receive_buffer); + + int asyncConsume( + const Name &name, + std::shared_ptr> receive_buffer); + + void asyncSendInterest(Interest::Ptr &&interest, + Portal::ConsumerCallback *callback); + + void stop(); + + void resume(); + + asio::io_service &getIoService() override; + + int setSocketOption(int socket_option_key, + uint32_t socket_option_value) override; + + int setSocketOption(int socket_option_key, + double socket_option_value) override; + + int setSocketOption(int socket_option_key, bool socket_option_value) override; + + int setSocketOption(int socket_option_key, Name socket_option_value) override; + + int setSocketOption(int socket_option_key, + std::list socket_option_value) override; + + int setSocketOption( + int socket_option_key, + ProducerContentObjectCallback socket_option_value) override; + + int setSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback socket_option_value) override; + + int setSocketOption( + int socket_option_key, + ConsumerContentObjectCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, + ConsumerInterestCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, + ProducerInterestCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, + ConsumerContentCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, + ConsumerManifestCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, + IcnObserver *socket_option_value) override; + + int setSocketOption(int socket_option_key, + HashAlgorithm socket_option_value) override; + + int setSocketOption(int socket_option_key, + utils::CryptoSuite crypto_suite) override; + + int setSocketOption(int socket_option_key, + const utils::Identity &crypto_suite) override; + + int setSocketOption(int socket_option_key, + const std::string &socket_option_value) override; + + int setSocketOption(int socket_option_key, + ConsumerTimerCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, + ProducerContentCallback socket_option_value) override; + + int getSocketOption(int socket_option_key, + uint32_t &socket_option_value) override; + + int getSocketOption(int socket_option_key, + double &socket_option_value) override; + + int getSocketOption(int socket_option_key, + bool &socket_option_value) override; + + int getSocketOption(int socket_option_key, + Name &socket_option_value) override; + + int getSocketOption(int socket_option_key, + std::list &socket_option_value) override; + + int getSocketOption( + int socket_option_key, + ProducerContentObjectCallback &socket_option_value) override; + + int getSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback &socket_option_value) override; + + int getSocketOption( + int socket_option_key, + ConsumerContentObjectCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ConsumerInterestCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ProducerInterestCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ConsumerContentCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ConsumerManifestCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + std::shared_ptr &socket_option_value) override; + + int getSocketOption(int socket_option_key, + IcnObserver **socket_option_value) override; + + int getSocketOption(int socket_option_key, + HashAlgorithm &socket_option_value) override; + + int getSocketOption(int socket_option_key, + utils::CryptoSuite &crypto_suite) override; + + int getSocketOption(int socket_option_key, + utils::Identity &crypto_suite) override; + + int getSocketOption(int socket_option_key, + std::string &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ConsumerTimerCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ProducerContentCallback &socket_option_value) override; + + protected: + std::shared_ptr transport_protocol_; + + private: + // context inner state variables + asio::io_service internal_io_service_; + asio::io_service &io_service_; + + std::shared_ptr portal_; + + utils::EventThread async_downloader_; + + Name network_name_; + + int interest_lifetime_; + + double min_window_size_; + double max_window_size_; + double current_window_size_; + uint32_t max_retransmissions_; + size_t output_buffer_size_; + size_t input_buffer_size_; + + // RAAQM Parameters + + double minimum_drop_probability_; + unsigned int sample_number_; + double gamma_; + double beta_; + double drop_factor_; + + // Rate estimation parameters + double rate_estimation_alpha_; + IcnObserver *rate_estimation_observer_; + int rate_estimation_batching_parameter_; + int rate_estimation_choice_; + + bool is_async_; + + utils::Verifier verifier_; + PARCKeyId *key_id_; + bool verify_signature_; + + std::shared_ptr> content_buffer_; + + ConsumerInterestCallback on_interest_retransmission_; + ConsumerInterestCallback on_interest_output_; + ConsumerInterestCallback on_interest_timeout_; + ConsumerInterestCallback on_interest_satisfied_; + + ConsumerContentObjectCallback on_content_object_input_; + ConsumerContentObjectVerificationCallback on_content_object_verification_; + + ConsumerContentObjectCallback on_content_object_; + ConsumerManifestCallback on_manifest_; + + ConsumerContentCallback on_payload_retrieved_; + + ConsumerTimerCallback on_timer_expires_; + + // Virtual download for traffic generator + + bool virtual_download_; + bool rtt_stats_; + + Time t0_; + Time t1_; + asio::steady_timer timer_; + uint32_t timer_interval_milliseconds_; +}; + +} // namespace interface + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/socket_options_default_values.h b/libtransport/src/hicn/transport/interfaces/socket_options_default_values.h new file mode 100755 index 000000000..5fae1c484 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/socket_options_default_values.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace transport { + +namespace interface { + +namespace default_values { + +const uint32_t interest_lifetime = 1001; // milliseconds +const uint32_t content_object_expiry_time = + 0xffff; // milliseconds -> 50 seconds +const uint32_t content_object_packet_size = 1500; // The ethernet MTU +const uint32_t producer_socket_input_buffer_size = 150000; // Interests +const uint32_t producer_socket_output_buffer_size = 150000; // Content Object +const uint32_t log_2_default_buffer_size = 12; +const uint32_t signature_size = 260; // bytes +const uint32_t key_locator_size = 60; // bytes +const uint32_t limit_guard = 80; // bytes +const uint32_t min_window_size = 1; // Interests +const uint32_t max_window_size = 128000; // Interests +const uint32_t digest_size = 34; // bytes +const uint32_t max_out_of_order_segments = 3; // content object +const uint32_t never_expire_time = 0x0000ffff << 0x0f; + +// RAAQM +const int sample_number = 30; +const double gamma_value = 1; +const double beta_value = 0.8; +const double drop_factor = 0.2; +const double minimum_drop_probability = 0.00001; +const int path_id = 0; +const double rate_alpha = 0.8; + +// Vegas +const double alpha = 1 / 8; +const double beta = 1 / 4; +const uint16_t k = 4; +const std::chrono::milliseconds clock_granularity = + std::chrono::milliseconds(100); + +// maximum allowed values +const uint32_t transport_protocol_min_retransmissions = 0; +const uint32_t transport_protocol_max_retransmissions = 128; +const uint32_t max_content_object_size = 8096; + +} // namespace default_values + +} // namespace interface + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/socket_options_keys.h b/libtransport/src/hicn/transport/interfaces/socket_options_keys.h new file mode 100755 index 000000000..1afad2b48 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/socket_options_keys.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +namespace transport { + +namespace interface { + +typedef enum { + RAAQM = 0, + VEGAS = 1, + CBR = 3, + RTC = 4, +} TransportProtocolAlgorithms; + +typedef enum { + INPUT_BUFFER_SIZE = 101, + OUTPUT_BUFFER_SIZE = 102, + NETWORK_NAME = 103, + NAME_SUFFIX = 104, + MAX_INTEREST_RETX = 105, + DATA_PACKET_SIZE = 106, + INTEREST_LIFETIME = 107, + CONTENT_OBJECT_EXPIRY_TIME = 108, + KEY_LOCATOR = 110, + SIGNATURE_TYPE = 111, + MIN_WINDOW_SIZE = 112, + MAX_WINDOW_SIZE = 113, + CURRENT_WINDOW_SIZE = 114, + ASYNC_MODE = 115, + MAKE_MANIFEST = 116, + PORTAL = 117, + RUNNING = 118, + HASH_ALGORITHM = 119, + CRYPTO_SUITE = 120, + IDENTITY = 121, + CERTIFICATE = 122, + VERIFY_SIGNATURE = 123, + TIMER_INTERVAL = 124 +} GeneralTransportOptions; + +typedef enum { + SAMPLE_NUMBER = 201, + GAMMA_VALUE = 202, + BETA_VALUE = 203, + DROP_FACTOR = 204, + MINIMUM_DROP_PROBABILITY = 205, + PATH_ID = 206, + RTT_STATS = 207, +} RaaqmTransportOptions; + +typedef enum { + RATE_ESTIMATION_ALPHA = 301, + RATE_ESTIMATION_OBSERVER = 302, + RATE_ESTIMATION_BATCH_PARAMETER = 303, + RATE_ESTIMATION_CHOICE = 304, +} RateEstimationOptions; + +typedef enum { + INTEREST_OUTPUT = 401, + INTEREST_RETRANSMISSION = 402, + INTEREST_EXPIRED = 403, + INTEREST_SATISFIED = 404, + CONTENT_OBJECT_INPUT = 411, + MANIFEST_INPUT = 412, + CONTENT_OBJECT_TO_VERIFY = 413, + CONTENT_RETRIEVED = 414, + TIMER_EXPIRES = 415 +} ConsumerCallbacksOptions; + +typedef enum { + INTEREST_INPUT = 501, + INTEREST_DROP = 502, + INTEREST_PASS = 503, + CACHE_HIT = 506, + CACHE_MISS = 508, + NEW_CONTENT_OBJECT = 509, + CONTENT_OBJECT_SIGN = 513, + CONTENT_OBJECT_READY = 510, + CONTENT_OBJECT_OUTPUT = 511, + CONTENT_PRODUCED = 512 +} ProducerCallbacksOptions; + +typedef enum { OUTPUT_INTERFACE = 601 } DataLinkOptions; + +typedef enum { VIRTUAL_DOWNLOAD = 601, USE_CFG_FILE = 603 } OtherOptions; + +typedef enum { + SHA_256 = 701, + RSA_256 = 702, +} SignatureType; + +} // namespace interface + +} // end namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/interfaces/socket_producer.cc b/libtransport/src/hicn/transport/interfaces/socket_producer.cc new file mode 100755 index 000000000..69adc2b3f --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/socket_producer.cc @@ -0,0 +1,948 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +namespace transport { + +namespace interface { + +typedef std::chrono::time_point Time; +typedef std::chrono::microseconds TimeDuration; + +ProducerSocket::ProducerSocket() : ProducerSocket(internal_io_service_) {} + +ProducerSocket::ProducerSocket(asio::io_service &io_service) + : io_service_(io_service), + portal_(std::make_shared(io_service_)), + data_packet_size_(default_values::content_object_packet_size), + content_object_expiry_time_(default_values::content_object_expiry_time), + output_buffer_(default_values::producer_socket_output_buffer_size), + async_thread_(), + registration_status_(REGISTRATION_NOT_ATTEMPTED), + making_manifest_(false), + signature_type_(SHA_256), + hash_algorithm_(HashAlgorithm::SHA_256), + input_buffer_capacity_(default_values::producer_socket_input_buffer_size), + input_buffer_size_(0), + processing_thread_stop_(false), + listening_thread_stop_(false), + on_interest_input_(VOID_HANDLER), + on_interest_dropped_input_buffer_(VOID_HANDLER), + on_interest_inserted_input_buffer_(VOID_HANDLER), + on_interest_satisfied_output_buffer_(VOID_HANDLER), + on_interest_process_(VOID_HANDLER), + on_new_segment_(VOID_HANDLER), + on_content_object_to_sign_(VOID_HANDLER), + on_content_object_in_output_buffer_(VOID_HANDLER), + on_content_object_output_(VOID_HANDLER), + on_content_object_evicted_from_output_buffer_(VOID_HANDLER), + on_content_produced_(VOID_HANDLER) { + listening_thread_stop_ = false; +} + +ProducerSocket::~ProducerSocket() { + TRANSPORT_LOGI("Destroying the ProducerSocket"); + processing_thread_stop_ = true; + portal_->stopEventsLoop(); + + if (processing_thread_.joinable()) { + processing_thread_.join(); + } + + if (listening_thread_.joinable()) { + listening_thread_.join(); + } +} + +void ProducerSocket::connect() { + portal_->connect(false); + listening_thread_ = std::thread(std::bind(&ProducerSocket::listen, this)); +} + +void ProducerSocket::serveForever() { + if (listening_thread_.joinable()) { + listening_thread_.join(); + } +} + +void ProducerSocket::stop() { + TRANSPORT_LOGI("Calling stop for ProducerSocket"); + portal_->killConnection(); + portal_->stopEventsLoop(); +} + +void ProducerSocket::registerPrefix(const Prefix &producer_namespace) { + served_namespaces_.push_back(producer_namespace); +} + +void ProducerSocket::listen() { + registration_status_ = REGISTRATION_IN_PROGRESS; + bool first = true; + + for (core::Prefix &producer_namespace : served_namespaces_) { + if (first) { + core::BindConfig bind_config(producer_namespace, 1000); + portal_->bind(bind_config); + portal_->setProducerCallback(this); + first = !first; + } else { + portal_->registerRoute(producer_namespace); + } + } + + portal_->runEventsLoop(); +} + +void ProducerSocket::passContentObjectToCallbacks( + const std::shared_ptr &content_object) { + if (content_object) { + if (on_new_segment_ != VOID_HANDLER) { + on_new_segment_(*this, *content_object); + } + + if (on_content_object_to_sign_ != VOID_HANDLER) { + on_content_object_to_sign_(*this, *content_object); + } + + if (on_content_object_in_output_buffer_ != VOID_HANDLER) { + on_content_object_in_output_buffer_(*this, *content_object); + } + + output_buffer_.insert(content_object); + + if (on_content_object_output_ != VOID_HANDLER) { + on_content_object_output_(*this, *content_object); + } + +#ifndef PUSH_API + std::unordered_map>::iterator it; + + { + std::lock_guard lock(pending_interests_mtx_); + it = pending_interests_.find(content_object->getName()); + } + + if (it != pending_interests_.end()) { + content_object->setLocator(it->second->getLocator()); + portal_->sendContentObject(*content_object); + std::lock_guard lock(pending_interests_mtx_); + pending_interests_.erase(it); + } +#else + portal_->sendContentObject(*content_object); +#endif + } +} + +void ProducerSocket::produce(ContentObject &content_object) { + if (on_content_object_in_output_buffer_ != VOID_HANDLER) { + on_content_object_in_output_buffer_(*this, content_object); + } + + output_buffer_.insert(std::static_pointer_cast( + content_object.shared_from_this())); + + if (on_content_object_output_ != VOID_HANDLER) { + on_content_object_output_(*this, content_object); + } + +#ifndef PUSH_API + std::unordered_map>::iterator it; + + { + std::lock_guard lock(pending_interests_mtx_); + it = pending_interests_.find(content_object.getName()); + } + + if (it != pending_interests_.end()) { + content_object.setLocator(it->second->getLocator()); + portal_->sendContentObject(content_object); + std::lock_guard lock(pending_interests_mtx_); + pending_interests_.erase(it); + } +#else + portal_->sendContentObject(content_object); +#endif +} + +uint32_t ProducerSocket::produce(Name content_name, const uint8_t *buf, + size_t buffer_size, bool is_last, + uint32_t start_offset) { + if (TRANSPORT_EXPECT_FALSE(buffer_size == 0)) { + return 0; + } + + const std::size_t hash_size = 32; + + int bytes_segmented = 0; + std::size_t header_size; + std::size_t manifest_header_size = 0; + std::size_t signature_length = 0; + std::uint32_t final_block_number = 0; + + uint64_t free_space_for_content = 0; + + core::Packet::Format format; + + uint32_t current_segment = start_offset; + std::shared_ptr manifest; + bool is_last_manifest = false; + std::unique_ptr zero_hash; + + // TODO Manifest may still be used for indexing + if (making_manifest_ && !identity_) { + throw errors::RuntimeException( + "Making manifests without setting producer identity. Aborting."); + } + + core::Packet::Format hf_format = core::Packet::Format::HF_UNSPEC; + core::Packet::Format hf_format_ah = core::Packet::Format::HF_UNSPEC; + if (content_name.getType() == HNT_CONTIGUOUS_V4 || + content_name.getType() == HNT_IOV_V4) { + hf_format = core::Packet::Format::HF_INET_TCP; + hf_format_ah = core::Packet::Format::HF_INET_TCP_AH; + } else if (content_name.getType() == HNT_CONTIGUOUS_V6 || + content_name.getType() == HNT_IOV_V6) { + hf_format = core::Packet::Format::HF_INET6_TCP; + hf_format_ah = core::Packet::Format::HF_INET6_TCP_AH; + } else { + throw errors::RuntimeException("Unknown name format."); + } + + format = hf_format; + if (making_manifest_) { + format = hf_format; + manifest_header_size = core::Packet::getHeaderSizeFromFormat( + hf_format_ah, identity_->getSignatureLength()); + } else if (identity_) { + format = hf_format_ah; + signature_length = identity_->getSignatureLength(); + } + + header_size = core::Packet::getHeaderSizeFromFormat(format, signature_length); + + free_space_for_content = data_packet_size_ - header_size; + + uint32_t number_of_segments = + uint32_t(std::ceil(double(buffer_size) / double(free_space_for_content))); + + if (free_space_for_content * number_of_segments < buffer_size) { + number_of_segments++; + } + + if (making_manifest_) { + auto segment_in_manifest = static_cast( + std::floor(double(data_packet_size_ - manifest_header_size - + ContentObjectManifest::getManifestHeaderSize()) / + (4.0 + 32.0)) - + 1.0); + auto number_of_manifests = static_cast( + std::ceil(float(number_of_segments) / segment_in_manifest)); + final_block_number = number_of_segments + number_of_manifests - 1; + + manifest.reset(ContentObjectManifest::createManifest( + content_name.setSuffix(current_segment++), + core::ManifestVersion::VERSION_1, core::ManifestType::INLINE_MANIFEST, + hash_algorithm_, is_last_manifest, content_name, + core::NextSegmentCalculationStrategy::INCREMENTAL, + identity_->getSignatureLength())); + manifest->setLifetime(content_object_expiry_time_); + + if (is_last) { + manifest->setFinalBlockNumber(final_block_number); + } else { + manifest->setFinalBlockNumber(std::numeric_limits::max()); + } + + uint8_t hash[hash_size]; + std::memset(hash, 0, hash_size); + zero_hash = std::make_unique( + hash, hash_size, static_cast(hash_algorithm_)); + } + + for (unsigned int packaged_segments = 0; + packaged_segments < number_of_segments; packaged_segments++) { + if (making_manifest_) { + if (manifest->estimateManifestSize(2) > + data_packet_size_ - manifest_header_size) { + // Add next manifest + manifest->addSuffixHash(current_segment, *zero_hash); + + // Send the current manifest + manifest->encode(); + + identity_->getSigner().sign(*manifest); + + passContentObjectToCallbacks(manifest); + + // Create new manifest. The reference to the last manifest has been + // acquired in the passContentObjectToCallbacks function, so we can + // safely release this reference + manifest.reset(ContentObjectManifest::createManifest( + content_name.setSuffix(current_segment), + core::ManifestVersion::VERSION_1, + core::ManifestType::INLINE_MANIFEST, hash_algorithm_, + is_last_manifest, content_name, + core::NextSegmentCalculationStrategy::INCREMENTAL, + identity_->getSignatureLength())); + manifest->setLifetime(content_object_expiry_time_); + if (is_last) { + manifest->setFinalBlockNumber(final_block_number); + } else { + manifest->setFinalBlockNumber(std::numeric_limits::max()); + } + current_segment++; + } + } + + auto content_object = std::make_shared( + content_name.setSuffix(current_segment), format); + content_object->setLifetime(content_object_expiry_time_); + + if (!making_manifest_ && identity_) { + content_object->setSignatureSize(signature_length); + } + + if (packaged_segments == number_of_segments - 1) { + content_object->appendPayload(&buf[bytes_segmented], + buffer_size - bytes_segmented); + bytes_segmented += buffer_size - bytes_segmented; + + if (is_last && making_manifest_) { + is_last_manifest = true; + } else if (is_last) { + content_object->setRst(); + } + + } else { + content_object->appendPayload(&buf[bytes_segmented], + free_space_for_content); + bytes_segmented += free_space_for_content; + } + + if (making_manifest_) { + using namespace std::chrono_literals; + utils::CryptoHash hash = content_object->computeDigest(hash_algorithm_); + manifest->addSuffixHash(current_segment, hash); + } else if (identity_) { + identity_->getSigner().sign(*content_object); + } + + current_segment++; + passContentObjectToCallbacks(content_object); + } + + if (making_manifest_) { + if (is_last_manifest) { + manifest->setFinalManifest(is_last_manifest); + } + manifest->encode(); + // Time t0 = std::chrono::steady_clock::now(); + identity_->getSigner().sign(*manifest); + passContentObjectToCallbacks(manifest); + } + + if (on_content_produced_ != VOID_HANDLER) { + on_content_produced_(*this, std::make_error_code(std::errc(0)), + buffer_size); + } + + return current_segment; +} + +void ProducerSocket::asyncProduce(ContentObject &content_object) { + if (!async_thread_.stopped()) { + // async_thread_.add(std::bind(&ProducerSocket::produce, this, + // content_object)); + } +} + +// void ProducerSocket::asyncProduce(const Name &suffix, +// const uint8_t *buf, +// size_t buffer_size, +// AsyncProduceCallback && handler) { +// if (!async_thread_.stopped()) { +// async_thread_.add([this, buffer = buf, size = buffer_size, cb = +// std::move(handler)] () { +// uint64_t bytes_written = produce(suff, buffer, size, 0, false); +// auto ec = std::make_errc(0); +// cb(*this, ec, bytes_written); +// }); +// } +// } + +void ProducerSocket::asyncProduce(const Name &suffix, const uint8_t *buf, + size_t buffer_size) { + if (!async_thread_.stopped()) { + async_thread_.add( + [this, suff = suffix, buffer = buf, size = buffer_size]() { + produce(suff, buffer, size, true); + }); + } +} + +void ProducerSocket::asyncProduce( + const Name &suffix, utils::SharableVector &&output_buffer) { + if (!async_thread_.stopped()) { + async_thread_.add( + [this, suff = suffix, buffer = std::move(output_buffer)]() { + TRANSPORT_LOGI("FOR REAL!!!!!! --> Producing content with name %s", + suff.toString().c_str()); + produce(suff, &buffer[0], buffer.size(), true); + }); + } +} + +void ProducerSocket::onInterest(const Interest &interest) { + if (on_interest_input_ != VOID_HANDLER) { + on_interest_input_(*this, interest); + } + + const std::shared_ptr content_object = + output_buffer_.find(interest); + + if (content_object) { + if (on_interest_satisfied_output_buffer_ != VOID_HANDLER) { + on_interest_satisfied_output_buffer_(*this, interest); + } + + if (on_content_object_output_ != VOID_HANDLER) { + on_content_object_output_(*this, *content_object); + } + + portal_->sendContentObject(*content_object); + } else { +#ifndef PUSH_API + { + std::lock_guard lock(pending_interests_mtx_); + pending_interests_[interest.getName()] = + std::static_pointer_cast(interest.shared_from_this()); + } +#endif + + if (on_interest_process_ != VOID_HANDLER) { + // external_io_service_.post([this, &interest] () { + on_interest_process_(*this, interest); + // }); + } + } +} + +asio::io_service &ProducerSocket::getIoService() { return io_service_; } + +int ProducerSocket::setSocketOption(int socket_option_key, + uint32_t socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::DATA_PACKET_SIZE: + if (socket_option_value < default_values::max_content_object_size && + socket_option_value > 0) { + data_packet_size_ = socket_option_value; + return SOCKET_OPTION_SET; + } else { + return SOCKET_OPTION_NOT_SET; + } + + case GeneralTransportOptions::INPUT_BUFFER_SIZE: + if (socket_option_value >= 1) { + input_buffer_capacity_ = socket_option_value; + return SOCKET_OPTION_SET; + } else { + return SOCKET_OPTION_NOT_SET; + } + + case GeneralTransportOptions::OUTPUT_BUFFER_SIZE: + output_buffer_.setLimit(socket_option_value); + return SOCKET_OPTION_SET; + + case GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME: + content_object_expiry_time_ = socket_option_value; + return SOCKET_OPTION_SET; + + case GeneralTransportOptions::SIGNATURE_TYPE: + if (socket_option_value == SOCKET_OPTION_DEFAULT) { + signature_type_ = SHA_256; + } else { + signature_type_ = socket_option_value; + } + + if (signature_type_ == SHA_256 || signature_type_ == RSA_256) { + signature_size_ = 32; + } + + case ProducerCallbacksOptions::INTEREST_INPUT: + if (socket_option_value == VOID_HANDLER) { + on_interest_input_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ProducerCallbacksOptions::INTEREST_DROP: + if (socket_option_value == VOID_HANDLER) { + on_interest_dropped_input_buffer_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ProducerCallbacksOptions::INTEREST_PASS: + if (socket_option_value == VOID_HANDLER) { + on_interest_inserted_input_buffer_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ProducerCallbacksOptions::CACHE_HIT: + if (socket_option_value == VOID_HANDLER) { + on_interest_satisfied_output_buffer_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ProducerCallbacksOptions::CACHE_MISS: + if (socket_option_value == VOID_HANDLER) { + on_interest_process_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ProducerCallbacksOptions::NEW_CONTENT_OBJECT: + if (socket_option_value == VOID_HANDLER) { + on_new_segment_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ProducerCallbacksOptions::CONTENT_OBJECT_SIGN: + if (socket_option_value == VOID_HANDLER) { + on_content_object_to_sign_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ProducerCallbacksOptions::CONTENT_OBJECT_READY: + if (socket_option_value == VOID_HANDLER) { + on_content_object_in_output_buffer_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ProducerCallbacksOptions::CONTENT_OBJECT_OUTPUT: + if (socket_option_value == VOID_HANDLER) { + on_content_object_output_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ProducerSocket::setSocketOption(int socket_option_key, + double socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ProducerSocket::setSocketOption(int socket_option_key, + bool socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::MAKE_MANIFEST: + making_manifest_ = socket_option_value; + return SOCKET_OPTION_SET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ProducerSocket::setSocketOption(int socket_option_key, + Name socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ProducerSocket::setSocketOption(int socket_option_key, + std::list socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::NETWORK_NAME: + served_namespaces_ = socket_option_value; + default: + return SOCKET_OPTION_NOT_SET; + } + + return SOCKET_OPTION_SET; +} + +int ProducerSocket::setSocketOption( + int socket_option_key, ProducerContentObjectCallback socket_option_value) { + switch (socket_option_key) { + case ProducerCallbacksOptions::NEW_CONTENT_OBJECT: + on_new_segment_ = socket_option_value; + return SOCKET_OPTION_SET; + + case ProducerCallbacksOptions::CONTENT_OBJECT_SIGN: + on_content_object_to_sign_ = socket_option_value; + return SOCKET_OPTION_SET; + + case ProducerCallbacksOptions::CONTENT_OBJECT_READY: + on_content_object_in_output_buffer_ = socket_option_value; + return SOCKET_OPTION_SET; + + case ProducerCallbacksOptions::CONTENT_OBJECT_OUTPUT: + on_content_object_output_ = socket_option_value; + return SOCKET_OPTION_SET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ProducerSocket::setSocketOption( + int socket_option_key, ProducerInterestCallback socket_option_value) { + switch (socket_option_key) { + case ProducerCallbacksOptions::INTEREST_INPUT: + on_interest_input_ = socket_option_value; + return SOCKET_OPTION_SET; + + case ProducerCallbacksOptions::INTEREST_DROP: + on_interest_dropped_input_buffer_ = socket_option_value; + return SOCKET_OPTION_SET; + + case ProducerCallbacksOptions::INTEREST_PASS: + on_interest_inserted_input_buffer_ = socket_option_value; + return SOCKET_OPTION_SET; + + case ProducerCallbacksOptions::CACHE_HIT: + on_interest_satisfied_output_buffer_ = socket_option_value; + return SOCKET_OPTION_SET; + + case ProducerCallbacksOptions::CACHE_MISS: + on_interest_process_ = socket_option_value; + return SOCKET_OPTION_SET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ProducerSocket::setSocketOption( + int socket_option_key, ProducerContentCallback socket_option_value) { + switch (socket_option_key) { + case ProducerCallbacksOptions::CONTENT_PRODUCED: + on_content_produced_ = socket_option_value; + return SOCKET_OPTION_SET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ProducerSocket::setSocketOption( + int socket_option_key, ConsumerContentObjectCallback socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ProducerSocket::setSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ProducerSocket::setSocketOption( + int socket_option_key, ConsumerInterestCallback socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ProducerSocket::setSocketOption( + int socket_option_key, ConsumerContentCallback socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ProducerSocket::setSocketOption( + int socket_option_key, ConsumerManifestCallback socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ProducerSocket::setSocketOption(int socket_option_key, + HashAlgorithm socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::HASH_ALGORITHM: + hash_algorithm_ = socket_option_value; + return SOCKET_OPTION_SET; + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ProducerSocket::setSocketOption(int socket_option_key, + utils::CryptoSuite socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::CRYPTO_SUITE: + crypto_suite_ = socket_option_value; + return SOCKET_OPTION_SET; + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ProducerSocket::setSocketOption( + int socket_option_key, const utils::Identity &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::IDENTITY: + identity_.reset(); + identity_ = std::make_unique(socket_option_value); + return SOCKET_OPTION_SET; + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ProducerSocket::setSocketOption(int socket_option_key, + const std::string &socket_option_value) { + switch (socket_option_key) { + case DataLinkOptions::OUTPUT_INTERFACE: + output_interface_ = socket_option_value; + portal_->setOutputInterface(output_interface_); + return SOCKET_OPTION_SET; + } + + return SOCKET_OPTION_NOT_SET; +} + +int ProducerSocket::setSocketOption( + int socket_option_key, + interface::ConsumerTimerCallback socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ProducerSocket::getSocketOption(int socket_option_key, + uint32_t &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::INPUT_BUFFER_SIZE: + socket_option_value = input_buffer_capacity_; + return SOCKET_OPTION_GET; + + case GeneralTransportOptions::OUTPUT_BUFFER_SIZE: + socket_option_value = output_buffer_.getLimit(); + return SOCKET_OPTION_GET; + + case GeneralTransportOptions::DATA_PACKET_SIZE: + socket_option_value = data_packet_size_; + return SOCKET_OPTION_GET; + + case GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME: + socket_option_value = content_object_expiry_time_; + return SOCKET_OPTION_GET; + + case GeneralTransportOptions::SIGNATURE_TYPE: + socket_option_value = signature_type_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ProducerSocket::getSocketOption(int socket_option_key, + double &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ProducerSocket::getSocketOption(int socket_option_key, + bool &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::MAKE_MANIFEST: + socket_option_value = making_manifest_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ProducerSocket::getSocketOption(int socket_option_key, + Name &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ProducerSocket::getSocketOption(int socket_option_key, + std::list &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::NETWORK_NAME: + + socket_option_value = served_namespaces_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ProducerSocket::getSocketOption( + int socket_option_key, ProducerContentObjectCallback &socket_option_value) { + switch (socket_option_key) { + case ProducerCallbacksOptions::NEW_CONTENT_OBJECT: + socket_option_value = on_new_segment_; + return SOCKET_OPTION_GET; + + case ProducerCallbacksOptions::CONTENT_OBJECT_SIGN: + socket_option_value = on_content_object_to_sign_; + return SOCKET_OPTION_GET; + + case ProducerCallbacksOptions::CONTENT_OBJECT_READY: + socket_option_value = on_content_object_in_output_buffer_; + return SOCKET_OPTION_GET; + + case ProducerCallbacksOptions::CONTENT_OBJECT_OUTPUT: + socket_option_value = on_content_object_output_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ProducerSocket::getSocketOption( + int socket_option_key, ProducerContentCallback &socket_option_value) { + switch (socket_option_key) { + case ProducerCallbacksOptions::CONTENT_PRODUCED: + socket_option_value = on_content_produced_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ProducerSocket::getSocketOption( + int socket_option_key, ProducerInterestCallback &socket_option_value) { + switch (socket_option_key) { + case ProducerCallbacksOptions::INTEREST_INPUT: + socket_option_value = on_interest_input_; + return SOCKET_OPTION_GET; + + case ProducerCallbacksOptions::INTEREST_DROP: + socket_option_value = on_interest_dropped_input_buffer_; + return SOCKET_OPTION_GET; + + case ProducerCallbacksOptions::INTEREST_PASS: + socket_option_value = on_interest_inserted_input_buffer_; + return SOCKET_OPTION_GET; + + case CACHE_HIT: + socket_option_value = on_interest_satisfied_output_buffer_; + return SOCKET_OPTION_GET; + + case CACHE_MISS: + socket_option_value = on_interest_process_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ProducerSocket::getSocketOption( + int socket_option_key, ConsumerContentObjectCallback &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ProducerSocket::getSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ProducerSocket::getSocketOption( + int socket_option_key, ConsumerInterestCallback &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ProducerSocket::getSocketOption( + int socket_option_key, ConsumerContentCallback &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ProducerSocket::getSocketOption( + int socket_option_key, ConsumerManifestCallback &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ProducerSocket::getSocketOption( + int socket_option_key, std::shared_ptr &socket_option_value) { + switch (socket_option_key) { + case PORTAL: + socket_option_value = portal_; + return SOCKET_OPTION_GET; + } + + return SOCKET_OPTION_NOT_GET; +} + +int ProducerSocket::getSocketOption(int socket_option_key, + IcnObserver **socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ProducerSocket::setSocketOption(int socket_option_key, + IcnObserver *socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ProducerSocket::getSocketOption(int socket_option_key, + HashAlgorithm &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::HASH_ALGORITHM: + socket_option_value = hash_algorithm_; + return SOCKET_OPTION_GET; + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ProducerSocket::getSocketOption(int socket_option_key, + utils::CryptoSuite &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::HASH_ALGORITHM: + socket_option_value = crypto_suite_; + return SOCKET_OPTION_GET; + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ProducerSocket::getSocketOption(int socket_option_key, + utils::Identity &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::IDENTITY: + if (identity_) { + socket_option_value = *identity_; + return SOCKET_OPTION_SET; + } + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ProducerSocket::getSocketOption(int socket_option_key, + std::string &socket_option_value) { + switch (socket_option_key) { + case DataLinkOptions::OUTPUT_INTERFACE: + socket_option_value = output_interface_; + return SOCKET_OPTION_GET; + } + + return SOCKET_OPTION_NOT_GET; +} + +int ProducerSocket::getSocketOption( + int socket_option_key, + interface::ConsumerTimerCallback &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +} // namespace interface + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/socket_producer.h b/libtransport/src/hicn/transport/interfaces/socket_producer.h new file mode 100755 index 000000000..06c47d973 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/socket_producer.h @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define PUSH_API 1 + +#define REGISTRATION_NOT_ATTEMPTED 0 +#define REGISTRATION_SUCCESS 1 +#define REGISTRATION_FAILURE 2 +#define REGISTRATION_IN_PROGRESS 3 + +namespace transport { + +namespace interface { + +using namespace core; + +class ProducerSocket : public Socket, + public BasePortal::ProducerCallback { + public: + explicit ProducerSocket(); + explicit ProducerSocket(asio::io_service &io_service); + + ~ProducerSocket(); + + void connect() override; + + uint32_t produce(Name content_name, const uint8_t *buffer, size_t buffer_size, + bool is_last = true, uint32_t start_offset = 0); + + void produce(ContentObject &content_object); + + void asyncProduce(const Name &suffix, const uint8_t *buf, size_t buffer_size); + + void asyncProduce(const Name &suffix, + utils::SharableVector &&output_buffer); + + void asyncProduce(ContentObject &content_object); + + void registerPrefix(const Prefix &producer_namespace); + + void serveForever(); + + void stop(); + + asio::io_service &getIoService() override; + + virtual void onInterest(const Interest &interest); + + virtual void onInterest(Interest::Ptr &&interest) override { + onInterest(*interest); + }; + + int setSocketOption(int socket_option_key, + uint32_t socket_option_value) override; + + int setSocketOption(int socket_option_key, + double socket_option_value) override; + + int setSocketOption(int socket_option_key, bool socket_option_value) override; + + int setSocketOption(int socket_option_key, Name socket_option_value) override; + + int setSocketOption(int socket_option_key, + std::list socket_option_value) override; + + int setSocketOption( + int socket_option_key, + ProducerContentObjectCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, + ProducerInterestCallback socket_option_value) override; + + int setSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback socket_option_value) override; + + int setSocketOption( + int socket_option_key, + ConsumerContentObjectCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, + ConsumerInterestCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, + ConsumerContentCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, + ConsumerManifestCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, IcnObserver *obs) override; + + int setSocketOption(int socket_option_key, + HashAlgorithm socket_option_value) override; + + int setSocketOption(int socket_option_key, + utils::CryptoSuite socket_option_value) override; + + int setSocketOption(int socket_option_key, + const utils::Identity &socket_option_value) override; + + int setSocketOption(int socket_option_key, + const std::string &socket_option_value) override; + + int setSocketOption(int socket_option_key, + ConsumerTimerCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, + ProducerContentCallback socket_option_value) override; + + int getSocketOption(int socket_option_key, + uint32_t &socket_option_value) override; + + int getSocketOption(int socket_option_key, + double &socket_option_value) override; + + int getSocketOption(int socket_option_key, + bool &socket_option_value) override; + + int getSocketOption(int socket_option_key, + Name &socket_option_value) override; + + int getSocketOption(int socket_option_key, + std::list &socket_option_value) override; + + int getSocketOption( + int socket_option_key, + ProducerContentObjectCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ProducerInterestCallback &socket_option_value) override; + + int getSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback &socket_option_value) override; + + int getSocketOption( + int socket_option_key, + ConsumerContentObjectCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ConsumerInterestCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ConsumerContentCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ConsumerManifestCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + std::shared_ptr &socket_option_value) override; + + int getSocketOption(int socket_option_key, + IcnObserver **socket_option_value) override; + + int getSocketOption(int socket_option_key, + HashAlgorithm &socket_option_value) override; + + int getSocketOption(int socket_option_key, + utils::CryptoSuite &socket_option_value) override; + + int getSocketOption(int socket_option_key, + utils::Identity &socket_option_value) override; + + int getSocketOption(int socket_option_key, + std::string &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ProducerContentCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ConsumerTimerCallback &socket_option_value) override; + + protected: + asio::io_service internal_io_service_; + asio::io_service &io_service_; + std::shared_ptr portal_; + std::size_t data_packet_size_; + std::list served_namespaces_; + uint32_t content_object_expiry_time_; + + // buffers + utils::ContentStore output_buffer_; + + private: + utils::EventThread async_thread_; + + int registration_status_; + + bool making_manifest_; + + // map for storing sequence numbers for several calls of the publish function + std::unordered_map> seq_number_map_; + + int signature_type_; + int signature_size_; + + HashAlgorithm hash_algorithm_; + utils::CryptoSuite crypto_suite_; + std::unique_ptr identity_; + // utils::Signer& signer_; + + // buffers + + std::queue> input_buffer_; + std::atomic_size_t input_buffer_capacity_; + std::atomic_size_t input_buffer_size_; + +#ifndef PUSH_API + std::mutex pending_interests_mtx_; + std::unordered_map> pending_interests_; +#endif + + // threads + std::thread listening_thread_; + std::thread processing_thread_; + volatile bool processing_thread_stop_; + volatile bool listening_thread_stop_; + + // callbacks + protected: + ProducerInterestCallback on_interest_input_; + ProducerInterestCallback on_interest_dropped_input_buffer_; + ProducerInterestCallback on_interest_inserted_input_buffer_; + ProducerInterestCallback on_interest_satisfied_output_buffer_; + ProducerInterestCallback on_interest_process_; + + ProducerContentObjectCallback on_new_segment_; + ProducerContentObjectCallback on_content_object_to_sign_; + ProducerContentObjectCallback on_content_object_in_output_buffer_; + ProducerContentObjectCallback on_content_object_output_; + ProducerContentObjectCallback on_content_object_evicted_from_output_buffer_; + + ProducerContentCallback on_content_produced_; + + private: + void listen(); + + void passContentObjectToCallbacks( + const std::shared_ptr &content_object); +}; + +} // namespace interface + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/portability/CMakeLists.txt b/libtransport/src/hicn/transport/portability/CMakeLists.txt new file mode 100755 index 000000000..eee973c2d --- /dev/null +++ b/libtransport/src/hicn/transport/portability/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/c_portability.h + ${CMAKE_CURRENT_SOURCE_DIR}/portability.h +) + +list(APPEND SOURCE_FILES + "" +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/libtransport/src/hicn/transport/portability/c_portability.h b/libtransport/src/hicn/transport/portability/c_portability.h new file mode 100755 index 000000000..71e976a81 --- /dev/null +++ b/libtransport/src/hicn/transport/portability/c_portability.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright 2017 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +// noinline +#ifdef _MSC_VER +#define TRANSPORT_NOINLINE __declspec(noinline) +#elif defined(__clang__) || defined(__GNUC__) +#define TRANSPORT_NOINLINE __attribute__((__noinline__)) +#else +#define TRANSPORT_NOINLINE +#endif + +// always inline +#ifdef _MSC_VER +#define TRANSPORT_ALWAYS_INLINE __forceinline +#elif defined(__clang__) || defined(__GNUC__) +#define TRANSPORT_ALWAYS_INLINE inline __attribute__((__always_inline__)) +#else +#define TRANSPORT_ALWAYS_INLINE inline +#endif \ No newline at end of file diff --git a/libtransport/src/hicn/transport/portability/portability.h b/libtransport/src/hicn/transport/portability/portability.h new file mode 100755 index 000000000..7063e1822 --- /dev/null +++ b/libtransport/src/hicn/transport/portability/portability.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright 2017 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include + +namespace portability { + +constexpr bool little_endian_arch = __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__; +constexpr bool big_endian_arch = !little_endian_arch; + +#if defined(__GNUC__) +#define _TRANSPORT_GNU_DISABLE_WARNING(warning) #warning +#define TRANSPORT_GNU_DISABLE_WARNING(warning) \ + _Pragma(_TRANSPORT_GNU_DISABLE_WARNING(GCC diagnostic ignored warning)) + +#ifdef __clang__ +#define TRANSPORT_CLANG_DISABLE_WARNING(warning) \ + TRANSPORT_GNU_DISABLE_WARNING(warning) +#define TRANSPORT_GCC_DISABLE_WARNING(warning) +#else +#define TRANSPORT_CLANG_DISABLE_WARNING(warning) +#define TRANSPORT_GCC_DISABLE_WARNING(warning) \ + TRANSPORT_GNU_DISABLE_WARNING(warning) +#endif +#endif + +} // namespace portability diff --git a/libtransport/src/hicn/transport/protocols/CMakeLists.txt b/libtransport/src/hicn/transport/protocols/CMakeLists.txt new file mode 100755 index 000000000..1c3b76c24 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/CMakeLists.txt @@ -0,0 +1,46 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/rate_estimation.h + ${CMAKE_CURRENT_SOURCE_DIR}/download_observer.h + ${CMAKE_CURRENT_SOURCE_DIR}/vegas.h + ${CMAKE_CURRENT_SOURCE_DIR}/protocol.h + ${CMAKE_CURRENT_SOURCE_DIR}/raaqm.h + ${CMAKE_CURRENT_SOURCE_DIR}/vegas_rto_estimator.h + ${CMAKE_CURRENT_SOURCE_DIR}/raaqm_data_path.h + ${CMAKE_CURRENT_SOURCE_DIR}/cbr.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_data_path.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/vegas.cc + ${CMAKE_CURRENT_SOURCE_DIR}/protocol.cc + ${CMAKE_CURRENT_SOURCE_DIR}/raaqm.cc + ${CMAKE_CURRENT_SOURCE_DIR}/vegas_rto_estimator.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rate_estimation.cc + ${CMAKE_CURRENT_SOURCE_DIR}/raaqm_data_path.cc + ${CMAKE_CURRENT_SOURCE_DIR}/cbr.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_data_path.cc +) + +set(TRANSPORT_CONFIG + ${CMAKE_CURRENT_SOURCE_DIR}/consumer.conf +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/libtransport/src/hicn/transport/protocols/cbr.cc b/libtransport/src/hicn/transport/protocols/cbr.cc new file mode 100755 index 000000000..3da4819c3 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/cbr.cc @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace transport { + +namespace protocol { + +using namespace interface; + +CbrTransportProtocol::CbrTransportProtocol(BaseSocket *icnet_socket) + : VegasTransportProtocol(icnet_socket) {} + +void CbrTransportProtocol::start( + utils::SharableVector &receive_buffer) { + current_window_size_ = socket_->current_window_size_; + VegasTransportProtocol::start(receive_buffer); +} + +void CbrTransportProtocol::changeInterestLifetime(uint64_t segment) { return; } + +void CbrTransportProtocol::increaseWindow() {} + +void CbrTransportProtocol::decreaseWindow() {} + +void CbrTransportProtocol::afterDataUnsatisfied(uint64_t segment) {} + +void CbrTransportProtocol::afterContentReception( + const Interest &interest, const ContentObject &content_object) {} + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/cbr.h b/libtransport/src/hicn/transport/protocols/cbr.h new file mode 100755 index 000000000..0a572292a --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/cbr.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +namespace transport { + +namespace protocol { + +class CbrTransportProtocol : public VegasTransportProtocol { + public: + CbrTransportProtocol(interface::BaseSocket *icnet_socket); + + void start(utils::SharableVector &receive_buffer) override; + + private: + void afterContentReception(const Interest &interest, + const ContentObject &content_object) override; + + void afterDataUnsatisfied(uint64_t segment) override; + + void increaseWindow() override; + + void decreaseWindow() override; + + void changeInterestLifetime(uint64_t segment) override; +}; + +} // end namespace protocol + +} // end namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/protocols/consumer.conf b/libtransport/src/hicn/transport/protocols/consumer.conf new file mode 100755 index 000000000..1a366f32f --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/consumer.conf @@ -0,0 +1,21 @@ +; this file contais the parameters for RAAQM +autotune = no +lifetime = 500 +retransmissions = 128 +beta = 0.99 +drop = 0.003 +beta_wifi_ = 0.99 +drop_wifi_ = 0.6 +beta_lte_ = 0.99 +drop_lte_ = 0.003 +wifi_delay_ = 200 +lte_delay_ = 9000 + +alpha = 0.95 +batching_parameter = 200 + +;Choice of rate estimator: +;0 --> an estimation each $(batching_parameter) packets +;1 --> an estimation "a la TCP", estimation at the end of the download of the segment + +rate_estimator = 0 diff --git a/libtransport/src/hicn/transport/protocols/download_observer.h b/libtransport/src/hicn/transport/protocols/download_observer.h new file mode 100755 index 000000000..6d24fe6fd --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/download_observer.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +namespace transport { + +namespace protocol { + +class IcnObserver { + public: + virtual ~IcnObserver(){}; + + virtual void notifyStats(double throughput) = 0; + virtual void notifyDownloadTime(double downloadTime) = 0; +}; + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/protocol.cc b/libtransport/src/hicn/transport/protocols/protocol.cc new file mode 100755 index 000000000..ea4fd6dbf --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/protocol.cc @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace transport { + +namespace protocol { + +TransportProtocol::TransportProtocol(interface::BaseSocket *icn_socket) + : socket_(dynamic_cast(icn_socket)), + is_running_(false), + interest_pool_() { + // Create pool of interests + increasePoolSize(); +} + +TransportProtocol::~TransportProtocol() {} + +void TransportProtocol::updatePortal() { portal_ = socket_->portal_; } + +bool TransportProtocol::isRunning() { return is_running_; } + +void TransportProtocol::increasePoolSize(std::size_t size) { + for (std::size_t i = 0; i < size; i++) { + interest_pool_.add(new Interest()); + } +} + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/protocol.h b/libtransport/src/hicn/transport/protocols/protocol.h new file mode 100755 index 000000000..56c57e025 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/protocol.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +namespace transport { + +namespace protocol { + +using namespace core; + +class TransportProtocolCallback { + virtual void onContentObject(const core::Interest &interest, + const core::ContentObject &content_object) = 0; + virtual void onTimeout(const core::Interest &interest) = 0; +}; + +class TransportProtocol : public interface::BasePortal::ConsumerCallback { + static constexpr std::size_t interest_pool_size = 4096; + + public: + TransportProtocol(interface::BaseSocket *icn_socket); + + virtual ~TransportProtocol(); + + void updatePortal(); + + bool isRunning(); + + virtual void start(utils::SharableVector &content_buffer) = 0; + + virtual void stop() = 0; + + virtual void resume() = 0; + + protected: + virtual void increasePoolSize(std::size_t size = interest_pool_size); + + TRANSPORT_ALWAYS_INLINE Interest::Ptr getInterest() { + auto result = interest_pool_.get(); + + while (TRANSPORT_EXPECT_FALSE(!result.first)) { + // Add packets to the pool + increasePoolSize(); + result = interest_pool_.get(); + } + + return std::move(result.second); + } + // Consumer Callback + virtual void onContentObject(Interest::Ptr &&i, ContentObject::Ptr &&c) = 0; + virtual void onTimeout(Interest::Ptr &&i) = 0; + + protected: + interface::ConsumerSocket *socket_; + std::shared_ptr portal_; + volatile bool is_running_; + utils::ObjectPool interest_pool_; +}; + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/raaqm.cc b/libtransport/src/hicn/transport/protocols/raaqm.cc new file mode 100755 index 000000000..cd22ecfdc --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/raaqm.cc @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +namespace transport { + +namespace protocol { + +using namespace interface; + +RaaqmTransportProtocol::RaaqmTransportProtocol(BaseSocket *icnet_socket) + : VegasTransportProtocol(icnet_socket), rate_estimator_(NULL) { + init(); +} + +RaaqmTransportProtocol::~RaaqmTransportProtocol() { + if (this->rate_estimator_) { + delete this->rate_estimator_; + } +} + +void RaaqmTransportProtocol::init() { + std::ifstream is(RAAQM_CONFIG_PATH); + + std::string line; + + socket_->beta_ = default_values::beta_value; + socket_->drop_factor_ = default_values::drop_factor; + socket_->interest_lifetime_ = default_values::interest_lifetime; + socket_->max_retransmissions_ = + default_values::transport_protocol_max_retransmissions; + raaqm_autotune_ = false; + default_beta_ = default_values::beta_value; + default_drop_ = default_values::drop_factor; + beta_wifi_ = default_values::beta_value; + drop_wifi_ = default_values::drop_factor; + beta_lte_ = default_values::beta_value; + drop_lte_ = default_values::drop_factor; + wifi_delay_ = 1000; + lte_delay_ = 15000; + + if (!is) { + TRANSPORT_LOGW("WARNING: RAAQM parameters not found, set default values"); + return; + } + + while (getline(is, line)) { + std::string command; + std::istringstream line_s(line); + + line_s >> command; + + if (command == ";") { + continue; + } + + if (command == "autotune") { + std::string tmp; + std::string val; + line_s >> tmp >> val; + if (val == "yes") { + raaqm_autotune_ = true; + } else { + raaqm_autotune_ = false; + } + continue; + } + + if (command == "lifetime") { + std::string tmp; + uint32_t lifetime; + line_s >> tmp >> lifetime; + socket_->interest_lifetime_ = lifetime; + continue; + } + + if (command == "retransmissions") { + std::string tmp; + uint32_t rtx; + line_s >> tmp >> rtx; + socket_->max_retransmissions_ = rtx; + continue; + } + + if (command == "beta") { + std::string tmp; + line_s >> tmp >> default_beta_; + socket_->beta_ = default_beta_; + continue; + } + + if (command == "drop") { + std::string tmp; + line_s >> tmp >> default_drop_; + socket_->drop_factor_ = default_drop_; + continue; + } + + if (command == "beta_wifi_") { + std::string tmp; + line_s >> tmp >> beta_wifi_; + continue; + } + + if (command == "drop_wifi_") { + std::string tmp; + line_s >> tmp >> drop_wifi_; + continue; + } + + if (command == "beta_lte_") { + std::string tmp; + line_s >> tmp >> beta_lte_; + continue; + } + + if (command == "drop_lte_") { + std::string tmp; + line_s >> tmp >> drop_lte_; + continue; + } + + if (command == "wifi_delay_") { + std::string tmp; + line_s >> tmp >> wifi_delay_; + continue; + } + + if (command == "lte_delay_") { + std::string tmp; + line_s >> tmp >> lte_delay_; + continue; + } + if (command == "alpha") { + std::string tmp; + double rate_alpha = 0.0; + line_s >> tmp >> rate_alpha; + socket_->rate_estimation_alpha_ = rate_alpha; + continue; + } + + if (command == "batching_parameter") { + std::string tmp; + uint32_t batching_param = 0; + line_s >> tmp >> batching_param; + socket_->rate_estimation_batching_parameter_ = batching_param; + continue; + } + + if (command == "rate_estimator") { + std::string tmp; + uint32_t choice_param = 0; + line_s >> tmp >> choice_param; + socket_->rate_estimation_choice_ = choice_param; + continue; + } + } + is.close(); +} + +void RaaqmTransportProtocol::start( + utils::SharableVector &content_buffer) { + if (this->rate_estimator_) { + this->rate_estimator_->onStart(); + } + + if (!cur_path_) { + double drop_factor; + double minimum_drop_probability; + uint32_t sample_number; + uint32_t interest_lifetime; + // double beta; + + drop_factor = socket_->drop_factor_; + minimum_drop_probability = socket_->minimum_drop_probability_; + sample_number = socket_->sample_number_; + interest_lifetime = socket_->interest_lifetime_; + // beta = socket_->beta_; + + double alpha = 0.0; + uint32_t batching_param = 0; + uint32_t choice_param = 0; + alpha = socket_->rate_estimation_alpha_; + batching_param = socket_->rate_estimation_batching_parameter_; + choice_param = socket_->rate_estimation_choice_; + + if (choice_param == 1) { + this->rate_estimator_ = new ALaTcpEstimator(); + } else { + this->rate_estimator_ = new SimpleEstimator(alpha, batching_param); + } + + this->rate_estimator_->observer_ = socket_->rate_estimation_observer_; + + cur_path_ = std::make_shared( + drop_factor, minimum_drop_probability, interest_lifetime * 1000, + sample_number); + path_table_[default_values::path_id] = cur_path_; + } + + VegasTransportProtocol::start(content_buffer); +} + +void RaaqmTransportProtocol::copyContent(const ContentObject &content_object) { + if (TRANSPORT_EXPECT_FALSE( + (content_object.getName().getSuffix() == final_block_number_) || + !(is_running_))) { + this->rate_estimator_->onDownloadFinished(); + } + VegasTransportProtocol::copyContent(content_object); +} + +void RaaqmTransportProtocol::updatePathTable( + const ContentObject &content_object) { + uint32_t path_id = content_object.getPathLabel(); + + if (path_table_.find(path_id) == path_table_.end()) { + if (cur_path_) { + // Create a new path with some default param + if (path_table_.empty()) { + throw errors::RuntimeException( + "No path initialized for path table, error could be in default " + "path initialization."); + } else { + // Initiate the new path default param + std::shared_ptr new_path = + std::make_shared( + *(path_table_.at(default_values::path_id))); + // Insert the new path into hash table + path_table_[path_id] = new_path; + } + } else { + throw errors::RuntimeException( + "UNEXPECTED ERROR: when running,current path not found."); + } + } + + cur_path_ = path_table_[path_id]; + + size_t header_size = content_object.headerSize(); + size_t data_size = content_object.payloadSize(); + + // Update measurements for path + cur_path_->updateReceivedStats(header_size + data_size, data_size); +} + +void RaaqmTransportProtocol::updateRtt(uint64_t segment) { + if (TRANSPORT_EXPECT_FALSE(!cur_path_)) { + throw std::runtime_error("ERROR: no current path found, exit"); + } else { + std::chrono::microseconds rtt; + + std::chrono::steady_clock::duration duration = + std::chrono::steady_clock::now() - + interest_timepoints_[segment & mask_]; + rtt = std::chrono::duration_cast(duration); + + if (this->rate_estimator_) { + this->rate_estimator_->onRttUpdate(rtt.count()); + } + cur_path_->insertNewRtt(rtt.count()); + cur_path_->smoothTimer(); + + if (cur_path_->newPropagationDelayAvailable()) { + check_drop_probability(); + } + } +} + +void RaaqmTransportProtocol::changeInterestLifetime(uint64_t segment) { + return; +} + +void RaaqmTransportProtocol::check_drop_probability() { + if (!raaqm_autotune_) { + return; + } + + unsigned int max_pd = 0; + std::unordered_map>::iterator it; + for (auto it = path_table_.begin(); it != path_table_.end(); ++it) { + if (it->second->getPropagationDelay() > max_pd && + it->second->getPropagationDelay() != UINT_MAX && + !it->second->isStale()) { + max_pd = it->second->getPropagationDelay(); + } + } + + double drop_prob = 0; + double beta = 0; + if (max_pd < wifi_delay_) { // only ethernet paths + drop_prob = default_drop_; + beta = default_beta_; + } else if (max_pd < lte_delay_) { // at least one wifi path + drop_prob = drop_wifi_; + beta = beta_wifi_; + } else { // at least one lte path + drop_prob = drop_lte_; + beta = beta_lte_; + } + + double old_drop_prob = 0; + double old_beta = 0; + old_beta = socket_->beta_; + old_drop_prob = socket_->drop_factor_; + + if (drop_prob == old_drop_prob && beta == old_beta) { + return; + } + + socket_->beta_ = beta; + socket_->drop_factor_ = drop_prob; + + for (it = path_table_.begin(); it != path_table_.end(); it++) { + it->second->setDropProb(drop_prob); + } +} + +void RaaqmTransportProtocol::check_for_stale_paths() { + if (!raaqm_autotune_) { + return; + } + + bool stale = false; + std::unordered_map>::iterator it; + for (it = path_table_.begin(); it != path_table_.end(); ++it) { + if (it->second->isStale()) { + stale = true; + break; + } + } + if (stale) { + check_drop_probability(); + } +} + +void RaaqmTransportProtocol::onTimeout(Interest::Ptr &&interest) { + check_for_stale_paths(); + VegasTransportProtocol::onTimeout(std::move(interest)); +} + +void RaaqmTransportProtocol::increaseWindow() { + double max_window_size = socket_->max_window_size_; + if (current_window_size_ < max_window_size) { + double gamma = socket_->gamma_; + + current_window_size_ += gamma / current_window_size_; + socket_->current_window_size_ = current_window_size_; + } + this->rate_estimator_->onWindowIncrease(current_window_size_); +} + +void RaaqmTransportProtocol::decreaseWindow() { + double min_window_size = socket_->min_window_size_; + if (current_window_size_ > min_window_size) { + double beta = socket_->beta_; + + current_window_size_ = current_window_size_ * beta; + if (current_window_size_ < min_window_size) { + current_window_size_ = min_window_size; + } + + socket_->current_window_size_ = current_window_size_; + } + this->rate_estimator_->onWindowDecrease(current_window_size_); +} + +void RaaqmTransportProtocol::RAAQM() { + if (!cur_path_) { + throw errors::RuntimeException("ERROR: no current path found, exit"); + exit(EXIT_FAILURE); + } else { + // Change drop probability according to RTT statistics + cur_path_->updateDropProb(); + + if (rand() % 10000 <= cur_path_->getDropProb() * 10000) { + decreaseWindow(); + } + } +} + +void RaaqmTransportProtocol::afterDataUnsatisfied(uint64_t segment) { + // Decrease the window because the timeout happened + decreaseWindow(); +} + +void RaaqmTransportProtocol::afterContentReception( + const Interest &interest, const ContentObject &content_object) { + updatePathTable(content_object); + increaseWindow(); + updateRtt(interest.getName().getSuffix()); + this->rate_estimator_->onDataReceived((int)content_object.payloadSize() + + content_object.headerSize()); + // Set drop probablility and window size accordingly + RAAQM(); +} + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/raaqm.h b/libtransport/src/hicn/transport/protocols/raaqm.h new file mode 100755 index 000000000..6ca410251 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/raaqm.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +namespace transport { + +namespace protocol { + +class RaaqmTransportProtocol : public VegasTransportProtocol { + public: + RaaqmTransportProtocol(interface::BaseSocket *icnet_socket); + + ~RaaqmTransportProtocol(); + + void start(utils::SharableVector &content_buffer) override; + + protected: + void copyContent(const ContentObject &content_object) override; + + private: + void init(); + + void afterContentReception(const Interest &interest, + const ContentObject &content_object) override; + + void afterDataUnsatisfied(uint64_t segment) override; + + void increaseWindow() override; + + void updateRtt(uint64_t segment); + + void decreaseWindow() override; + + void changeInterestLifetime(uint64_t segment) override; + + void onTimeout(Interest::Ptr &&interest) override; + + void RAAQM(); + + void updatePathTable(const ContentObject &content_object); + + void check_drop_probability(); + + void check_for_stale_paths(); + + void printRtt(); + + /** + * Current download path + */ + std::shared_ptr cur_path_; + + /** + * Hash table for path: each entry is a pair path ID(key) - path object + */ + std::unordered_map> path_table_; + + bool set_interest_filter_; + // for rate-estimation at packet level + IcnRateEstimator *rate_estimator_; + + // params for autotuning + bool raaqm_autotune_; + double default_beta_; + double default_drop_; + double beta_wifi_; + double drop_wifi_; + double beta_lte_; + double drop_lte_; + unsigned int wifi_delay_; + unsigned int lte_delay_; +}; + +} // end namespace protocol + +} // end namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/protocols/raaqm_data_path.cc b/libtransport/src/hicn/transport/protocols/raaqm_data_path.cc new file mode 100755 index 000000000..f876cf4f8 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/raaqm_data_path.cc @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +namespace transport { + +namespace protocol { + +RaaqmDataPath::RaaqmDataPath(double drop_factor, + double minimum_drop_probability, + unsigned new_timer, unsigned int samples, + uint64_t new_rtt, uint64_t new_rtt_min, + uint64_t new_rtt_max, unsigned new_pd) + + : drop_factor_(drop_factor), + minimum_drop_probability_(minimum_drop_probability), + timer_(new_timer), + samples_(samples), + rtt_(new_rtt), + rtt_min_(new_rtt_min), + rtt_max_(new_rtt_max), + prop_delay_(new_pd), + new_prop_delay_(false), + drop_prob_(0), + packets_received_(0), + last_packets_received_(0), + m_packets_bytes_received_(0), + last_packets_bytes_received_(0), + raw_data_bytes_received_(0), + last_raw_data_bytes_received_(0), + rtt_samples_(samples_), + average_rtt_(0), + alpha_(ALPHA) { + gettimeofday(&m_last_received_pkt_, 0); +} + +RaaqmDataPath &RaaqmDataPath::insertNewRtt(uint64_t new_rtt) { + rtt_ = new_rtt; + rtt_samples_.pushBack(new_rtt); + + rtt_max_ = rtt_samples_.rBegin(); + rtt_min_ = rtt_samples_.begin(); + + if (rtt_min_ < prop_delay_) { + new_prop_delay_ = true; + prop_delay_ = rtt_min_; + } + + gettimeofday(&m_last_received_pkt_, 0); + + return *this; +} + +RaaqmDataPath &RaaqmDataPath::updateReceivedStats(std::size_t packet_size, + std::size_t data_size) { + packets_received_++; + m_packets_bytes_received_ += packet_size; + raw_data_bytes_received_ += data_size; + + return *this; +} + +double RaaqmDataPath::getDropFactor() { return drop_factor_; } + +double RaaqmDataPath::getDropProb() { return drop_prob_; } + +RaaqmDataPath &RaaqmDataPath::setDropProb(double dropProb) { + drop_prob_ = dropProb; + + return *this; +} + +double RaaqmDataPath::getMinimumDropProbability() { + return minimum_drop_probability_; +} + +double RaaqmDataPath::getTimer() { return timer_; } + +RaaqmDataPath &RaaqmDataPath::smoothTimer() { + timer_ = (1 - TIMEOUT_SMOOTHER) * timer_ + + (TIMEOUT_SMOOTHER)*rtt_ * (TIMEOUT_RATIO); + + return *this; +} + +double RaaqmDataPath::getRtt() { return rtt_; } + +double RaaqmDataPath::getAverageRtt() { return average_rtt_; } + +double RaaqmDataPath::getRttMax() { return rtt_max_; } + +double RaaqmDataPath::getRttMin() { return rtt_min_; } + +unsigned RaaqmDataPath::getSampleValue() { return samples_; } + +unsigned RaaqmDataPath::getRttQueueSize() { + return static_cast(rtt_samples_.size()); +} + +RaaqmDataPath &RaaqmDataPath::updateDropProb() { + drop_prob_ = 0.0; + + if (getSampleValue() == getRttQueueSize()) { + if (rtt_max_ == rtt_min_) { + drop_prob_ = minimum_drop_probability_; + } else { + drop_prob_ = minimum_drop_probability_ + + drop_factor_ * (rtt_ - rtt_min_) / (rtt_max_ - rtt_min_); + } + } + + return *this; +} + +double RaaqmDataPath::getMicroSeconds(struct timeval &time) { + return (double)(time.tv_sec) * 1000000 + (double)(time.tv_usec); +} + +void RaaqmDataPath::setAlpha(double alpha) { + if (alpha >= 0 && alpha <= 1) { + alpha_ = alpha; + } +} + +bool RaaqmDataPath::newPropagationDelayAvailable() { + bool r = new_prop_delay_; + new_prop_delay_ = false; + return r; +} + +unsigned int RaaqmDataPath::getPropagationDelay() { return prop_delay_; } + +bool RaaqmDataPath::isStale() { + struct timeval now; + gettimeofday(&now, 0); + double time = getMicroSeconds(now) - getMicroSeconds(m_last_received_pkt_); + if (time > 2000000) { + return true; + } + return false; +} + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/raaqm_data_path.h b/libtransport/src/hicn/transport/protocols/raaqm_data_path.h new file mode 100755 index 000000000..6f63940c9 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/raaqm_data_path.h @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include +#include + +#define TIMEOUT_SMOOTHER 0.1 +#define TIMEOUT_RATIO 10 +#define ALPHA 0.8 + +namespace transport { + +namespace protocol { + +class RaaqmDataPath { + public: + RaaqmDataPath(double drop_factor, double minimum_drop_probability, + unsigned new_timer, unsigned int samples, + uint64_t new_rtt = 1000, uint64_t new_rtt_min = 1000, + uint64_t new_rtt_max = 1000, unsigned new_pd = UINT_MAX); + + public: + /* + * @brief Add a new RTT to the RTT queue of the path, check if RTT queue is + * full, and thus need overwrite. Also it maintains the validity of min and + * max of RTT. + * @param new_rtt is the value of the new RTT + */ + RaaqmDataPath &insertNewRtt(uint64_t new_rtt); + + /** + * @brief Update the path statistics + * @param packet_size the size of the packet received, including the ICN + * header + * @param data_size the size of the data received, without the ICN header + */ + RaaqmDataPath &updateReceivedStats(std::size_t packet_size, + std::size_t data_size); + + /** + * @brief Get the value of the drop factor parameter + */ + double getDropFactor(); + + /** + * @brief Get the value of the drop probability + */ + double getDropProb(); + + /** + * @brief Set the value pf the drop probability + * @param drop_prob is the value of the drop probability + */ + RaaqmDataPath &setDropProb(double drop_prob); + + /** + * @brief Get the minimum drop probability + */ + double getMinimumDropProbability(); + + /** + * @brief Get last RTT + */ + double getRtt(); + + /** + * @brief Get average RTT + */ + double getAverageRtt(); + + /** + * @brief Get the current m_timer value + */ + double getTimer(); + + /** + * @brief Smooth he value of the m_timer accordingly with the last RTT + * measured + */ + RaaqmDataPath &smoothTimer(); + + /** + * @brief Get the maximum RTT among the last samples + */ + double getRttMax(); + + /** + * @brief Get the minimum RTT among the last samples + */ + double getRttMin(); + + /** + * @brief Get the number of saved samples + */ + unsigned getSampleValue(); + + /** + * @brief Get the size og the RTT queue + */ + unsigned getRttQueueSize(); + + /* + * @brief Change drop probability according to RTT statistics + * Invoked in RAAQM(), before control window size update. + */ + RaaqmDataPath &updateDropProb(); + + /** + * @brief This function convert the time from struct timeval to its value in + * microseconds + */ + static double getMicroSeconds(struct timeval &time); + + void setAlpha(double alpha); + + /** + * @brief Returns the smallest RTT registered so far for this path + */ + + unsigned int getPropagationDelay(); + + bool newPropagationDelayAvailable(); + + bool isStale(); + + private: + /** + * The value of the drop factor + */ + double drop_factor_; + + /** + * The minumum drop probability + */ + double minimum_drop_probability_; + + /** + * The timer, expressed in milliseconds + */ + double timer_; + + /** + * The number of samples to store for computing the protocol measurements + */ + const unsigned int samples_; + + /** + * The last, the minimum and the maximum value of the RTT (among the last + * m_samples samples) + */ + uint64_t rtt_, rtt_min_, rtt_max_, prop_delay_; + + bool new_prop_delay_; + + /** + * The current drop probability + */ + double drop_prob_; + + /** + * The number of packets received in this path + */ + intmax_t packets_received_; + + /** + * The first packet received after the statistics print + */ + intmax_t last_packets_received_; + + /** + * Total number of bytes received including the ICN header + */ + intmax_t m_packets_bytes_received_; + + /** + * The amount of packet bytes received at the last path summary computation + */ + intmax_t last_packets_bytes_received_; + + /** + * Total number of bytes received without including the ICN header + */ + intmax_t raw_data_bytes_received_; + + /** + * The amount of raw dat bytes received at the last path summary computation + */ + intmax_t last_raw_data_bytes_received_; + + class byArrival; + + class byOrder; + + /** + * Double ended queue for the RTTs + */ + + typedef utils::MinFilter RTTQueue; + + RTTQueue rtt_samples_; + + /** + * Time of the last call to the path reporter method + */ + struct timeval m_last_received_pkt_; + + double average_rtt_; + double alpha_; +}; + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/rate_estimation.cc b/libtransport/src/hicn/transport/protocols/rate_estimation.cc new file mode 100755 index 000000000..e313bf9f6 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/rate_estimation.cc @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace transport { + +namespace protocol { + +void *Timer(void *data) { + InterRttEstimator *estimator = (InterRttEstimator *)data; + + double dat_rtt, my_avg_win, my_avg_rtt; + int my_win_change, number_of_packets, max_packet_size; + + pthread_mutex_lock(&(estimator->mutex_)); + dat_rtt = estimator->rtt_; + pthread_mutex_unlock(&(estimator->mutex_)); + + while (estimator->is_running_) { + usleep(KV * dat_rtt); + + pthread_mutex_lock(&(estimator->mutex_)); + + dat_rtt = estimator->rtt_; + my_avg_win = estimator->avg_win_; + my_avg_rtt = estimator->avg_rtt_; + my_win_change = estimator->win_change_; + number_of_packets = estimator->number_of_packets_; + max_packet_size = estimator->max_packet_size_; + estimator->avg_rtt_ = estimator->rtt_; + estimator->avg_win_ = 0; + estimator->win_change_ = 0; + estimator->number_of_packets_ = 1; + + pthread_mutex_unlock(&(estimator->mutex_)); + + if (number_of_packets == 0 || my_win_change == 0) { + continue; + } + if (estimator->estimation_ == 0) { + estimator->estimation_ = (my_avg_win * 8.0 * max_packet_size * 1000000.0 / + (1.0 * my_win_change)) / + (my_avg_rtt / (1.0 * number_of_packets)); + } + + estimator->estimation_ = + estimator->alpha_ * estimator->estimation_ + + (1 - estimator->alpha_) * ((my_avg_win * 8.0 * max_packet_size * + 1000000.0 / (1.0 * my_win_change)) / + (my_avg_rtt / (1.0 * number_of_packets))); + + if (estimator->observer_) { + estimator->observer_->notifyStats(estimator->estimation_); + } + } + + return nullptr; +} + +InterRttEstimator::InterRttEstimator(double alpha_arg) { + this->estimated_ = false; + this->observer_ = NULL; + this->alpha_ = alpha_arg; + this->thread_is_running_ = false; + this->my_th_ = NULL; + this->is_running_ = true; + this->avg_rtt_ = 0.0; + this->estimation_ = 0.0; + this->avg_win_ = 0.0; + this->rtt_ = 0.0; + this->win_change_ = 0; + this->number_of_packets_ = 0; + this->max_packet_size_ = 0; + this->win_current_ = 1.0; + + pthread_mutex_init(&(this->mutex_), NULL); + gettimeofday(&(this->start_time_), 0); + gettimeofday(&(this->begin_batch_), 0); +} + +InterRttEstimator::~InterRttEstimator() { + this->is_running_ = false; + if (this->my_th_) { + pthread_join(*(this->my_th_), NULL); + } + this->my_th_ = NULL; + pthread_mutex_destroy(&(this->mutex_)); +} + +void InterRttEstimator::onRttUpdate(double rtt) { + pthread_mutex_lock(&(this->mutex_)); + this->rtt_ = rtt; + this->number_of_packets_++; + this->avg_rtt_ += rtt; + pthread_mutex_unlock(&(this->mutex_)); + + if (!thread_is_running_) { + my_th_ = (pthread_t *)malloc(sizeof(pthread_t)); + if (!my_th_) { + TRANSPORT_LOGE("Error allocating thread."); + my_th_ = NULL; + } + if (/*int err = */ pthread_create(my_th_, NULL, transport::protocol::Timer, + (void *)this)) { + TRANSPORT_LOGE("Error creating the thread"); + my_th_ = NULL; + } + thread_is_running_ = true; + } +} + +void InterRttEstimator::onWindowIncrease(double win_current) { + timeval end; + gettimeofday(&end, 0); + double delay = RaaqmDataPath::getMicroSeconds(end) - + RaaqmDataPath::getMicroSeconds(this->begin_batch_); + + pthread_mutex_lock(&(this->mutex_)); + this->avg_win_ += this->win_current_ * delay; + this->win_current_ = win_current; + this->win_change_ += delay; + pthread_mutex_unlock(&(this->mutex_)); + + gettimeofday(&(this->begin_batch_), 0); +} + +void InterRttEstimator::onWindowDecrease(double win_current) { + timeval end; + gettimeofday(&end, 0); + double delay = RaaqmDataPath::getMicroSeconds(end) - + RaaqmDataPath::getMicroSeconds(this->begin_batch_); + + pthread_mutex_lock(&(this->mutex_)); + this->avg_win_ += this->win_current_ * delay; + this->win_current_ = win_current; + this->win_change_ += delay; + pthread_mutex_unlock(&(this->mutex_)); + + gettimeofday(&(this->begin_batch_), 0); +} + +ALaTcpEstimator::ALaTcpEstimator() { + this->estimation_ = 0.0; + this->observer_ = NULL; + gettimeofday(&(this->start_time_), 0); + this->totalSize_ = 0.0; +} + +void ALaTcpEstimator::onStart() { + this->totalSize_ = 0.0; + gettimeofday(&(this->start_time_), 0); +} + +void ALaTcpEstimator::onDownloadFinished() { + timeval end; + gettimeofday(&end, 0); + double delay = RaaqmDataPath::getMicroSeconds(end) - + RaaqmDataPath::getMicroSeconds(this->start_time_); + this->estimation_ = this->totalSize_ * 8 * 1000000 / delay; + if (observer_) { + observer_->notifyStats(this->estimation_); + } +} + +void ALaTcpEstimator::onDataReceived(int packet_size) { + this->totalSize_ += packet_size; +} + +SimpleEstimator::SimpleEstimator(double alphaArg, int batching_param) { + this->estimation_ = 0.0; + this->estimated_ = false; + this->observer_ = NULL; + this->batching_param_ = batching_param; + this->total_size_ = 0.0; + this->number_of_packets_ = 0; + this->base_alpha_ = alphaArg; + this->alpha_ = alphaArg; + gettimeofday(&(this->start_time_), 0); + gettimeofday(&(this->begin_batch_), 0); +} + +void SimpleEstimator::onStart() { + this->estimated_ = false; + this->number_of_packets_ = 0; + this->total_size_ = 0.0; + gettimeofday(&(this->begin_batch_), 0); + gettimeofday(&(this->start_time_), 0); +} + +void SimpleEstimator::onDownloadFinished() { + timeval end; + gettimeofday(&end, 0); + double delay = RaaqmDataPath::getMicroSeconds(end) - + RaaqmDataPath::getMicroSeconds(this->start_time_); + if (observer_) { + observer_->notifyDownloadTime(delay); + } + if (!this->estimated_) { + // Assuming all packets carry max_packet_size_ bytes of data + // (8*max_packet_size_ bits); 1000000 factor to convert us to seconds + if (this->estimation_) { + this->estimation_ = + alpha_ * this->estimation_ + + (1 - alpha_) * (total_size_ * 8 * 1000000.0 / (delay)); + } else { + this->estimation_ = total_size_ * 8 * 1000000.0 / (delay); + } + if (observer_) { + observer_->notifyStats(this->estimation_); + } + this->alpha_ = this->base_alpha_ * (((double)this->number_of_packets_) / + ((double)this->batching_param_)); + } else { + if (this->number_of_packets_ >= + (int)(75.0 * (double)this->batching_param_ / 100.0)) { + delay = RaaqmDataPath::getMicroSeconds(end) - + RaaqmDataPath::getMicroSeconds(this->begin_batch_); + // Assuming all packets carry max_packet_size_ bytes of data + // (8*max_packet_size_ bits); 1000000 factor to convert us to seconds + if (this->estimation_) { + this->estimation_ = + alpha_ * this->estimation_ + + (1 - alpha_) * (total_size_ * 8 * 1000000.0 / (delay)); + } else { + this->estimation_ = total_size_ * 8 * 1000000.0 / (delay); + } + if (observer_) { + observer_->notifyStats(this->estimation_); + } + this->alpha_ = this->base_alpha_ * (((double)this->number_of_packets_) / + ((double)this->batching_param_)); + } + } + this->number_of_packets_ = 0; + this->total_size_ = 0.0; + gettimeofday(&(this->begin_batch_), 0); + gettimeofday(&(this->start_time_), 0); +} + +void SimpleEstimator::onDataReceived(int packet_size) { + this->total_size_ += packet_size; +} + +void SimpleEstimator::onRttUpdate(double rtt) { + this->number_of_packets_++; + + if (number_of_packets_ == this->batching_param_) { + timeval end; + gettimeofday(&end, 0); + double delay = RaaqmDataPath::getMicroSeconds(end) - + RaaqmDataPath::getMicroSeconds(this->begin_batch_); + // Assuming all packets carry max_packet_size_ bytes of data + // (8*max_packet_size_ bits); 1000000 factor to convert us to seconds + if (this->estimation_) { + this->estimation_ = + alpha_ * this->estimation_ + + (1 - alpha_) * (total_size_ * 8 * 1000000.0 / (delay)); + } else { + this->estimation_ = total_size_ * 8 * 1000000.0 / (delay); + } + if (observer_) { + observer_->notifyStats(this->estimation_); + } + this->alpha_ = this->base_alpha_; + this->number_of_packets_ = 0; + this->total_size_ = 0.0; + gettimeofday(&(this->begin_batch_), 0); + } +} + +BatchingPacketsEstimator::BatchingPacketsEstimator(double alpha_arg, + int param) { + this->estimated_ = false; + this->observer_ = NULL; + this->alpha_ = alpha_arg; + this->batching_param_ = param; + this->number_of_packets_ = 0; + this->avg_win_ = 0.0; + this->avg_rtt_ = 0.0; + this->win_change_ = 0.0; + this->max_packet_size_ = 0; + this->estimation_ = 0.0; + this->win_current_ = 1.0; + gettimeofday(&(this->begin_batch_), 0); + gettimeofday(&(this->start_time_), 0); +} + +void BatchingPacketsEstimator::onRttUpdate(double rtt) { + this->number_of_packets_++; + this->avg_rtt_ += rtt; + + if (number_of_packets_ == this->batching_param_) { + if (estimation_ == 0) { + estimation_ = (avg_win_ * 8.0 * max_packet_size_ * 1000000.0 / + (1.0 * win_change_)) / + (avg_rtt_ / (1.0 * number_of_packets_)); + } else { + estimation_ = alpha_ * estimation_ + + (1 - alpha_) * ((avg_win_ * 8.0 * max_packet_size_ * + 1000000.0 / (1.0 * win_change_)) / + (avg_rtt_ / (1.0 * number_of_packets_))); + } + + if (observer_) { + observer_->notifyStats(estimation_); + } + + this->number_of_packets_ = 0; + this->avg_win_ = 0.0; + this->avg_rtt_ = 0.0; + this->win_change_ = 0.0; + } +} + +void BatchingPacketsEstimator::onWindowIncrease(double win_current) { + timeval end; + gettimeofday(&end, 0); + double delay = RaaqmDataPath::getMicroSeconds(end) - + RaaqmDataPath::getMicroSeconds(this->begin_batch_); + this->avg_win_ += this->win_current_ * delay; + this->win_current_ = win_current; + this->win_change_ += delay; + gettimeofday(&(this->begin_batch_), 0); +} + +void BatchingPacketsEstimator::onWindowDecrease(double win_current) { + timeval end; + gettimeofday(&end, 0); + double delay = RaaqmDataPath::getMicroSeconds(end) - + RaaqmDataPath::getMicroSeconds(this->begin_batch_); + this->avg_win_ += this->win_current_ * delay; + this->win_current_ = win_current; + this->win_change_ += delay; + gettimeofday(&(this->begin_batch_), 0); +} + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/rate_estimation.h b/libtransport/src/hicn/transport/protocols/rate_estimation.h new file mode 100755 index 000000000..b889efe12 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/rate_estimation.h @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include + +#define BATCH 50 +#define KV 20 +#define ALPHA 0.8 +#define RATE_CHOICE 0 + +namespace transport { + +namespace protocol { + +class IcnRateEstimator { + public: + IcnRateEstimator(){}; + + virtual ~IcnRateEstimator(){}; + + virtual void onRttUpdate(double rtt){}; + + virtual void onDataReceived(int packetSize){}; + + virtual void onWindowIncrease(double winCurrent){}; + + virtual void onWindowDecrease(double winCurrent){}; + + virtual void onStart(){}; + + virtual void onDownloadFinished(){}; + + virtual void setObserver(IcnObserver *observer) { + this->observer_ = observer; + }; + IcnObserver *observer_; + struct timeval start_time_; + struct timeval begin_batch_; + double base_alpha_; + double alpha_; + double estimation_; + int number_of_packets_; + // this boolean is to make sure at least one estimation of the BW is done + bool estimated_; +}; + +// A rate estimator RTT-based. Computes EWMA(WinSize)/EWMA(RTT) + +class InterRttEstimator : public IcnRateEstimator { + public: + InterRttEstimator(double alpha_arg); + + ~InterRttEstimator(); + + void onRttUpdate(double rtt); + + void onDataReceived(int packet_size) { + if (packet_size > this->max_packet_size_) { + this->max_packet_size_ = packet_size; + } + }; + + void onWindowIncrease(double win_current); + + void onWindowDecrease(double win_current); + + void onStart(){}; + + void onDownloadFinished(){}; + + // private: should be done by using getters + pthread_t *my_th_; + bool thread_is_running_; + double rtt_; + bool is_running_; + pthread_mutex_t mutex_; + double avg_rtt_; + double avg_win_; + int max_packet_size_; + double win_change_; + double win_current_; +}; + +// A rate estimator, Batching Packets based. Computes EWMA(WinSize)/EWMA(RTT) + +class BatchingPacketsEstimator : public IcnRateEstimator { + public: + BatchingPacketsEstimator(double alpha_arg, int batchingParam); + + void onRttUpdate(double rtt); + + void onDataReceived(int packet_size) { + if (packet_size > this->max_packet_size_) { + this->max_packet_size_ = packet_size; + } + }; + + void onWindowIncrease(double win_current); + + void onWindowDecrease(double win_current); + + void onStart(){}; + + void onDownloadFinished(){}; + + private: + int batching_param_; + double avg_rtt_; + double avg_win_; + double win_change_; + int max_packet_size_; + double win_current_; +}; + +// Segment Estimator + +class ALaTcpEstimator : public IcnRateEstimator { + public: + ALaTcpEstimator(); + + void onDataReceived(int packet_size); + void onStart(); + void onDownloadFinished(); + + private: + double totalSize_; +}; + +// A Rate estimator, this one is the simplest: counting batching_param_ packets +// and then divide the sum of the size of these packets by the time taken to DL +// them. Should be the one used + +class SimpleEstimator : public IcnRateEstimator { + public: + SimpleEstimator(double alpha, int batching_param); + + void onRttUpdate(double rtt); + + void onDataReceived(int packet_size); + + void onWindowIncrease(double win_current){}; + + void onWindowDecrease(double win_current){}; + + void onStart(); + + void onDownloadFinished(); + + private: + int batching_param_; + double total_size_; +}; + +void *Timer(void *data); + +} // namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/rtc.cc b/libtransport/src/hicn/transport/protocols/rtc.cc new file mode 100755 index 000000000..1f42cf230 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/rtc.cc @@ -0,0 +1,813 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +/* + * TODO + * 2) start/constructor/rest variable implementation + * 3) interest retransmission: now I always recover, we should recover only if + * we have enough time 4) returnContentToUser: rememeber to remove the first + * 32bits from the payload + */ + +namespace transport { + +namespace protocol { + +using namespace interface; + +RTCTransportProtocol::RTCTransportProtocol(BaseSocket *icnet_socket) + : TransportProtocol(icnet_socket), + inflightInterests_(1 << default_values::log_2_default_buffer_size), + modMask_((1 << default_values::log_2_default_buffer_size) - 1) { + icnet_socket->getSocketOption(PORTAL, portal_); + reset(); +} + +RTCTransportProtocol::~RTCTransportProtocol() { + if (is_running_) { + stop(); + } +} + +void RTCTransportProtocol::start( + utils::SharableVector &content_buffer) { + + if(is_running_) + return; + + is_running_ = true; + content_buffer_ = content_buffer.shared_from_this(); + + reset(); + scheduleNextInterest(); + + portal_->runEventsLoop(); + is_running_ = false; +} + +void RTCTransportProtocol::stop() { + if(!is_running_) + return; + + is_running_ = false; + portal_->stopEventsLoop(); +} + +void RTCTransportProtocol::resume(){ + if(is_running_) + return; + + is_running_ = true; + + lastRoundBegin_ = std::chrono::steady_clock::now(); + inflightInterestsCount_ = 0; + if(content_buffer_) + content_buffer_->clear(); + + scheduleNextInterest(); + + portal_->runEventsLoop(); + + is_running_ = false; +} + +void RTCTransportProtocol::onRTCPPacket(uint8_t *packet, size_t len) { + //#define MASK_RTCP_VERSION 192 + //#define MASK_TYPE_CODE 31 + size_t read = 0; + uint8_t *offset = packet; + while (read < len) { + if ((((*offset) & MASK_RTCP_VERSION) >> 6) != RTCP_VERSION) { + TRANSPORT_LOGE("error while parsing RTCP packet, version unkwown"); + return; + } + processRtcpHeader(offset); + uint16_t RTCPlen = (ntohs(*(((uint16_t *)offset) + 1)) + 1) * 4; + offset += RTCPlen; + read += RTCPlen; + } +} + +// private +void RTCTransportProtocol::reset() { + // controller var + lastRoundBegin_ = std::chrono::steady_clock::now(); + currentState_ = RTC_SYNC_STATE; + + // cwin var + currentCWin_ = INITIAL_CWIN; + maxCWin_ = INITIAL_CWIN_MAX; + + // names/packets var + actualSegment_ = 0; + inflightInterestsCount_ = 0; + while (interestRetransmissions_.size() != 0) interestRetransmissions_.pop(); + nackedByProducer_.clear(); + nackedByProducerMaxSize_ = 512; + if (content_buffer_) content_buffer_->clear(); + + holes_.clear(); + lastReceived_ = 0; + + // stats + receivedBytes_ = 0; + sentInterest_ = 0; + receivedData_ = 0; + packetLost_ = 0; + avgPacketSize_ = INIT_PACKET_SIZE; + gotNack_ = false; + gotFutureNack_ = 0; + roundsWithoutNacks_ = 0; + pathTable_.clear(); + // roundCounter_ = 0; + // minRTTwin_.clear(); + // for (int i = 0; i < MIN_RTT_WIN; i++) + // minRTTwin_.push_back(UINT_MAX); + minRtt_ = UINT_MAX; + + // CC var + estimatedBw_ = 0.0; + lossRate_ = 0.0; + queuingDelay_ = 0.0; + protocolState_ = RTC_NORMAL_STATE; + + producerPathLabel_ = 0; + socket_->setSocketOption( + GeneralTransportOptions::INTEREST_LIFETIME, + (uint32_t) + RTC_INTEREST_LIFETIME); // XXX this should bedone by the application +} + +uint32_t max(uint32_t a, uint32_t b) { + if (a > b) + return a; + else + return b; +} + +uint32_t min(uint32_t a, uint32_t b) { + if (a < b) + return a; + else + return b; +} + +void RTCTransportProtocol::checkRound() { + uint32_t duration = std::chrono::duration_cast( + std::chrono::steady_clock::now() - lastRoundBegin_) + .count(); + if (duration >= ROUND_LEN) { + lastRoundBegin_ = std::chrono::steady_clock::now(); + updateStats(duration); // update stats and window + } +} + +void RTCTransportProtocol::updateDelayStats( + const ContentObject &content_object) { + uint32_t segmentNumber = content_object.getName().getSuffix(); + uint32_t pkt = segmentNumber & modMask_; + + if (inflightInterests_[pkt].transmissionTime == + 0) // this is always the case if we have a retransmitted packet (timeout + // or RTCP) + return; + + uint32_t pathLabel = content_object.getPathLabel(); + + if (pathTable_.find(pathLabel) == pathTable_.end()) { + // found a new path + std::shared_ptr newPath = std::make_shared(); + pathTable_[pathLabel] = newPath; + } + + // RTT measurements are useful both from NACKs and data packets + uint64_t RTT = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count() - + inflightInterests_[pkt].transmissionTime; + + pathTable_[pathLabel]->insertRttSample(RTT); + + // we collect OWD only for datapackets + if (content_object.getPayload().length() != NACK_HEADER_SIZE) { + uint64_t *senderTimeStamp = (uint64_t *)content_object.getPayload().data(); + + int64_t OWD = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count() - + *senderTimeStamp; + + pathTable_[pathLabel]->insertOwdSample(OWD); + } +} + +void RTCTransportProtocol::updateStats(uint32_t round_duration) { + if (receivedBytes_ != 0) { + double bytesPerSec = (double)(receivedBytes_ * ((double)MILLI_IN_A_SEC / + (double)round_duration)); + estimatedBw_ = (estimatedBw_ * ESTIMATED_BW_ALPHA) + + ((1 - ESTIMATED_BW_ALPHA) * bytesPerSec); + } + + auto it = pathTable_.find(producerPathLabel_); + if (it == pathTable_.end()) return; + + // double maxAvgRTT = it->second->getAverageRtt(); + // double minRTT = it->second->getMinRtt(); + minRtt_ = it->second->getMinRtt(); + queuingDelay_ = it->second->getQueuingDealy(); + + if (minRtt_ == 0) minRtt_ = 1; + + for (auto it = pathTable_.begin(); it != pathTable_.end(); it++) { + it->second->roundEnd(); + } + + // this is inefficient but the window is supposed to be small, so it + // probably makes sense to leave it like this + // if(minRTT == 0) + // minRTT = 1; + + // minRTTwin_[roundCounter_ % MIN_RTT_WIN] = minRTT; + // minRtt_ = minRTT; + // for (int i = 0; i < MIN_RTT_WIN; i++) + // if(minRtt_ > minRTTwin_[i]) + // minRtt_ = minRTTwin_[i]; + + // roundCounter_++; + + // std::cout << "min RTT " << minRtt_ << " queuing " << queuingDelay_ << + // std::endl; + + if (sentInterest_ != 0 && currentState_ == RTC_NORMAL_STATE) { + double lossRate = (double)((double)packetLost_ / (double)sentInterest_); + lossRate_ = lossRate_ * ESTIMATED_LOSSES_ALPHA + + (lossRate * (1 - ESTIMATED_LOSSES_ALPHA)); + } + + if (avgPacketSize_ == 0) avgPacketSize_ = INIT_PACKET_SIZE; + + uint32_t BDP = + ceil((estimatedBw_ * (double)((double)minRtt_ / (double)MILLI_IN_A_SEC) * + BANDWIDTH_SLACK_FACTOR) / + avgPacketSize_); + uint32_t BW = ceil(estimatedBw_); + computeMaxWindow(BW, BDP); + + // bound also by interest lifitime* production rate + if (!gotNack_) { + roundsWithoutNacks_++; + if (currentState_ == RTC_SYNC_STATE && + roundsWithoutNacks_ >= ROUNDS_IN_SYNC_BEFORE_SWITCH) { + currentState_ = RTC_NORMAL_STATE; + } + } else { + roundsWithoutNacks_ = 0; + } + + updateCCState(); + updateWindow(); + + // in any case we reset all the counters + + gotNack_ = false; + gotFutureNack_ = 0; + receivedBytes_ = 0; + sentInterest_ = 0; + receivedData_ = 0; + packetLost_ = 0; +} + +void RTCTransportProtocol::updateCCState() { + // TODO +} + +void RTCTransportProtocol::computeMaxWindow(uint32_t productionRate, + uint32_t BDPWin) { + if (productionRate == + 0) // we have no info about the producer, keep the previous maxCWin + return; + + uint32_t interestLifetime = default_values::interest_lifetime; + socket_->getSocketOption(GeneralTransportOptions::INTEREST_LIFETIME, + interestLifetime); + uint32_t maxWaintingInterest = ceil( + (productionRate / avgPacketSize_) * + (double)((double)(interestLifetime * INTEREST_LIFETIME_REDUCTION_FACTOR) / + (double)MILLI_IN_A_SEC)); + + if (currentState_ == RTC_SYNC_STATE) { + // in this case we do not limit the window with the BDP, beacuse most likly + // it is wrong + maxCWin_ = maxWaintingInterest; + return; + } + + // currentState = RTC_NORMAL_STATE + if (BDPWin != 0) { + maxCWin_ = ceil((double)BDPWin + ((double)BDPWin / 10.0)); // BDP + 10% + } else { + maxCWin_ = min(maxWaintingInterest, maxCWin_); + } +} + +void RTCTransportProtocol::updateWindow() { + if (currentState_ == RTC_SYNC_STATE) return; + + if (currentCWin_ < maxCWin_ * 0.7) { + currentCWin_ = min(maxCWin_, currentCWin_ * WIN_INCREASE_FACTOR); + } else if (currentCWin_ > maxCWin_) { + currentCWin_ = max(currentCWin_ * WIN_DECREASE_FACTOR, MIN_CWIN); + } +} + +void RTCTransportProtocol::decreaseWindow() { + // this is used only in SYNC mode + if (currentState_ == RTC_NORMAL_STATE) return; + + if (gotFutureNack_ == 1) + currentCWin_ = + min((currentCWin_ - 1), ceil((double)maxCWin_ * 0.66)); // 2/3 + else + currentCWin_--; + + currentCWin_ = max(currentCWin_, MIN_CWIN); +} + +void RTCTransportProtocol::increaseWindow() { + // this is used only in SYNC mode + if (currentState_ == RTC_NORMAL_STATE) return; + + // we need to be carefull to do not increase the window to much + if (currentCWin_ < ((double)maxCWin_ * 0.5)) { + currentCWin_ = currentCWin_ + 1; // exponential + } else { + currentCWin_ = min( + maxCWin_, ceil(currentCWin_ + (1.0 / (double)currentCWin_))); // linear + } +} + +void RTCTransportProtocol::sendInterest() { + Name interest_name; + socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, + interest_name); + bool isRTX = false; + // uint32_t sentInt = 0; + + if (interestRetransmissions_.size() > 0) { + // handle retransmission + // here we have two possibile retransmissions: retransmissions due to + // timeouts and retransmissions due to RTCP NACKs. we will send the interest + // anyway, even if it is pending (this is possible only in the second case) + uint32_t rtxSeg = interestRetransmissions_.front(); + interestRetransmissions_.pop(); + + std::unordered_map::const_iterator res = + holes_.find(rtxSeg); + if (res != holes_.end()) { + // this packet is already managed by as an hole + // we don't need to send it again + return; + } + + // a packet recovery means that there was a loss + packetLost_++; + + uint32_t pkt = rtxSeg & modMask_; + interest_name.setSuffix(rtxSeg); + + // if the interest is not pending anymore we encrease the retrasnmission + // counter in order to avoid to handle a recovered packt as a normal one + if (!portal_->interestIsPending(interest_name)) { + inflightInterests_[pkt].retransmissions++; + } + + inflightInterests_[pkt].transmissionTime = 0; + isRTX = true; + } else { + // in this case we send the packet only if it is not pending yet + interest_name.setSuffix(actualSegment_); + if (portal_->interestIsPending(interest_name)) { + actualSegment_++; + return; + } + + // sentInt = actualSegment_; + uint32_t pkt = actualSegment_ & modMask_; + inflightInterests_[pkt].transmissionTime = + std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + inflightInterests_[pkt].retransmissions = 0; + actualSegment_++; + } + + auto interest = getInterest(); + interest->setName(interest_name); + + uint32_t interestLifetime = default_values::interest_lifetime; + socket_->getSocketOption(GeneralTransportOptions::INTEREST_LIFETIME, + interestLifetime); + interest->setLifetime(uint32_t(interestLifetime)); + + ConsumerInterestCallback on_interest_output = VOID_HANDLER; + + socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_OUTPUT, + on_interest_output); + + if (on_interest_output != VOID_HANDLER) { + on_interest_output(*dynamic_cast(socket_), *interest); + } + + if (TRANSPORT_EXPECT_FALSE(!is_running_)) { + return; + } + + using namespace std::placeholders; + portal_->sendInterest(std::move(interest)); + + sentInterest_++; + + if (!isRTX) { + inflightInterestsCount_++; + } +} + +void RTCTransportProtocol::scheduleNextInterest() { + checkRound(); + if(!is_running_) + return; + + uint32_t MAX_RECOVER = + 40; // if the packet is more than MAX_RECOVER seq in the past we drop it + uint64_t TIME_BEFORE_RECOVERY = 10; // this should be proporsional to the RTT + + // holes are important only in NORMAL state + if (currentState_ == RTC_NORMAL_STATE) { + for (std::unordered_map::iterator it = holes_.begin(); + it != holes_.end();) { + if (it->first < lastReceived_ - MAX_RECOVER) { + // the packet is to hold, remove it + it = holes_.erase(it); + } else { + uint64_t now = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + uint64_t sinceLastTry = now - it->second; + + if (sinceLastTry > TIME_BEFORE_RECOVERY || it->second == 0) { + // a recovery means a packet lost + packetLost_++; + // update last sent time + it->second = now; + + Name interest_name; + socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, + interest_name); + + uint32_t pkt = it->first & modMask_; + interest_name.setSuffix(it->first); + + if (!portal_->interestIsPending(interest_name)) { + inflightInterests_[pkt].retransmissions++; + } + + inflightInterests_[pkt].transmissionTime = 0; + // XXX + // code refactoring: + // from here on this is a copy and paste of the code inside + // sendInterest this should go inside an other method + auto interest = getInterest(); + uint32_t interestLifetime = default_values::interest_lifetime; + socket_->getSocketOption(GeneralTransportOptions::INTEREST_LIFETIME, + interestLifetime); + interest->setLifetime(uint32_t(interestLifetime)); + + ConsumerInterestCallback on_interest_output = VOID_HANDLER; + + socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_OUTPUT, + on_interest_output); + if (on_interest_output != VOID_HANDLER) + on_interest_output(*dynamic_cast(socket_), + *interest); + + if (TRANSPORT_EXPECT_FALSE(!is_running_)) return; + + using namespace std::placeholders; + portal_->sendInterest(std::move(interest)); + + sentInterest_++; + } + ++it; + } + // as usual check the round at each packet + checkRound(); + } + } + + while (interestRetransmissions_.size() > 0) { + sendInterest(); + checkRound(); + } + + while (inflightInterestsCount_ < currentCWin_) { + sendInterest(); + checkRound(); + } +} + +void RTCTransportProtocol::scheduleAppNackRtx(std::vector &nacks) { + for (uint32_t i = 0; i < nacks.size(); i++) { + if (nackedByProducer_.find(nacks[i]) != nackedByProducer_.end()) { + continue; + } + // packetLost_++; + // XXX here I need to avoid the retrasmission for packet that were nacked by + // the network + interestRetransmissions_.push(nacks[i]); + } + + scheduleNextInterest(); +} +void RTCTransportProtocol::onTimeout(Interest::Ptr &&interest) { + // packetLost_++; + + uint32_t segmentNumber = interest->getName().getSuffix(); + uint32_t pkt = segmentNumber & modMask_; + + if (inflightInterests_[pkt].retransmissions == 0) { + inflightInterestsCount_--; + } + + if (inflightInterests_[pkt].retransmissions < MAX_RTX) { + interestRetransmissions_.push(segmentNumber); + } + + scheduleNextInterest(); +} + +void RTCTransportProtocol::onNack(const ContentObject &content_object) { + uint32_t *payload = (uint32_t *)content_object.getPayload().data(); + uint32_t productionSeg = *payload; + uint32_t productionRate = *(++payload); + uint32_t nackSegment = content_object.getName().getSuffix(); + + // we synch the estimated production rate with the actual one + estimatedBw_ = (double)productionRate; + + // if(inflightInterests_[segmentNumber % + // default_values::default_buffer_size].retransmissions != 0){ ignore nacks for + // retransmissions + // return; + //} + + gotNack_ = true; + + if (productionSeg > nackSegment) { + // we are asking for stuff produced in the past + actualSegment_ = max(productionSeg + 1, actualSegment_); + if (currentState_ == RTC_NORMAL_STATE) { + currentState_ = RTC_SYNC_STATE; + // if we switch in SYNC mode we do not care about holes + // se we reset the data structure. going back to NORMAL + // mode will anable again the holes_ check. + holes_.clear(); + lastReceived_ = 0; + } + + computeMaxWindow(productionRate, 0); + increaseWindow(); + + if (nackedByProducer_.size() >= nackedByProducerMaxSize_) + nackedByProducer_.erase(nackedByProducer_.begin()); + nackedByProducer_.insert(nackSegment); + + } else if (productionSeg < nackSegment) { + gotFutureNack_++; + // we are asking stuff in the future + // example + // 10 12 13 14 15 16 17 + // ^ ^ ^ + // in prod nack actual + // in this example we sent up to segment 17 and we get a nack for segment 15 + // this means that we will get nack also for 16 17 + // and valid data for 13 14 + // so the next segment to ask is 15, because 13 and 14 will can back anyway + // we go back only in the case that the actual segment is really bigger than + // nack segment, other we do nothing + + actualSegment_ = min(actualSegment_, nackSegment); + + computeMaxWindow(productionRate, 0); + decreaseWindow(); + + if (currentState_ == RTC_SYNC_STATE) { + currentState_ = RTC_NORMAL_STATE; + } + } // equal should not happen +} + +void RTCTransportProtocol::onContentObject( + Interest::Ptr &&interest, ContentObject::Ptr &&content_object) { + uint32_t payload_size = content_object->getPayload().length(); + uint32_t segmentNumber = content_object->getName().getSuffix(); + uint32_t pkt = segmentNumber & modMask_; + + // try to recover holes + // we can recover haoles with valid data, nacks or retransmitted packets + bool recoveredHole = false; + std::unordered_map::const_iterator res = + holes_.find(segmentNumber); + if (res != holes_.end()) { + holes_.erase(res); + recoveredHole = true; + } + + if (payload_size == NACK_HEADER_SIZE) { + // Nacks always come form the producer, so we set the producerePathLabel_; + producerPathLabel_ = content_object->getPathLabel(); + if (inflightInterests_[pkt].retransmissions == 0) { + inflightInterestsCount_--; + onNack(*content_object); + updateDelayStats(*content_object); + } + + } else { + receivedData_++; + + avgPacketSize_ = + (ESTIMATED_PACKET_SIZE * avgPacketSize_) + + ((1 - ESTIMATED_PACKET_SIZE) * content_object->getPayload().length()); + + if (inflightInterests_[pkt].retransmissions == 0) { + inflightInterestsCount_--; + // we count only non retransmitted data in order to take into accunt only + // the transmition rate of the producer + receivedBytes_ += + content_object->headerSize() + content_object->payloadSize(); + updateDelayStats(*content_object); + + // handle holes + // the packet sequence make sense only in case of valid data (no nacks, no + // rtx) in RTC_NORMAL_STATE we should get all the packets in order, so if + // segmentNumber != lastReceived + 1 something happened + // if recoveredHole == true this is a packet recovered so we should do + // nothing + if (currentState_ == RTC_NORMAL_STATE && recoveredHole == false) { + if ((segmentNumber != lastReceived_ + 1) && + segmentNumber > lastReceived_) { + // we have holes in the sequence + for (uint32_t seq = lastReceived_ + 1; seq < segmentNumber; seq++) { + // the hole exists we do not insert it again + std::unordered_map::const_iterator res = + holes_.find(seq); + if (res == holes_.end()) + holes_.insert(std::make_pair(seq, 0)); // 0 means never sent + } + } + } + + // this if should be always true + if (segmentNumber > lastReceived_) { + lastReceived_ = segmentNumber; + } + } + + returnContentToUser(*content_object); + increaseWindow(); + } + + scheduleNextInterest(); +} + +void RTCTransportProtocol::returnContentToUser( + const ContentObject &content_object) { + // return content to the user + Array a = content_object.getPayload(); + + uint8_t *start = ((uint8_t *)a.data()) + TIMESTAMP_SIZE; + unsigned size = a.length() - TIMESTAMP_SIZE; + + // set offset between hICN and RTP packets + uint16_t rtp_seq = ntohs(*(((uint16_t *)start) + 1)); + RTPhICN_offset_ = content_object.getName().getSuffix() - rtp_seq; + + content_buffer_->insert(content_buffer_->end(), start, start + size); + + ConsumerContentCallback on_payload = VOID_HANDLER; + socket_->getSocketOption(CONTENT_RETRIEVED, on_payload); + if (on_payload != VOID_HANDLER) { + on_payload(*dynamic_cast(socket_), size, + std::make_error_code(std::errc(0))); + } +} + +uint32_t RTCTransportProtocol::hICN2RTP(uint32_t hicn_seq) { + return RTPhICN_offset_ - hicn_seq; +} + +uint32_t RTCTransportProtocol::RTP2hICN(uint32_t rtp_seq) { + return RTPhICN_offset_ + rtp_seq; +} + +void RTCTransportProtocol::processRtcpHeader(uint8_t *offset) { + uint8_t pkt_type = (*(offset + 1)); + switch (pkt_type) { + case RTCP_RR: // Receiver report + TRANSPORT_LOGI("got RR packet\n"); + break; + case RTCP_SR: // Sender report + TRANSPORT_LOGI("got SR packet\n"); + break; + case RTCP_SDES: // Description + processSDES(offset); + break; + case RTCP_RTPFB: // Transport layer FB message + processGenericNack(offset); + break; + case RTCP_PSFB: + processPli(offset); + break; + default: + errorParsingRtcpHeader(offset); + } +} + +void RTCTransportProtocol::errorParsingRtcpHeader(uint8_t *offset) { + uint8_t pt = (*(offset + 1)); + uint8_t code = ((*offset) & MASK_TYPE_CODE); + TRANSPORT_LOGE("Received unknwnon RTCP packet. Payload type = %u, code = %u", + pt, code); +} + +void RTCTransportProtocol::processSDES(uint8_t *offset) { + uint8_t code = ((*offset) & MASK_TYPE_CODE); + switch (code) { + case RTCP_SDES_CNAME: + TRANSPORT_LOGI("got SDES packet: CNAME\n"); + break; + default: + errorParsingRtcpHeader(offset); + } +} + +void RTCTransportProtocol::processPli(uint8_t *offset) { + if (((*offset) & MASK_TYPE_CODE) != RTCP_PSFB_PLI) { + errorParsingRtcpHeader(offset); + return; + } + + TRANSPORT_LOGI("got PLI packet\n"); +} + +void RTCTransportProtocol::processGenericNack(uint8_t *offset) { + if (((*offset) & MASK_TYPE_CODE) != RTCP_RTPFB_GENERIC_NACK) { + errorParsingRtcpHeader(offset); + return; + } + + std::vector nacks; + + uint16_t header_lines = + ntohs(*(((uint16_t *)offset) + 1)) - + 2; // 2 is the number of header 32-bits words - 1 (RFC 4885) + uint8_t *payload = offset + RTPC_NACK_HEADER; // 12 bytes + for (uint16_t l = header_lines; l > 0; l--) { + nacks.push_back(RTP2hICN(ntohs(*((uint16_t *)payload)))); + + uint16_t BLP = ntohs(*(((uint16_t *)payload) + 1)); + + for (int bit = 0; bit < 15; bit++) { // 16 bits word to scan + if ((BLP >> bit) & 1) { + nacks.push_back(RTP2hICN((ntohs(*((uint16_t *)payload)) + bit + 1) % + MAX_RTCP_SEQ_NUMBER)); + } + } + + payload += 4; // go to the next line + } + + portal_->getIoService().post(std::bind( + &RTCTransportProtocol::scheduleAppNackRtx, this, std::move(nacks))); +} + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/rtc.h b/libtransport/src/hicn/transport/protocols/rtc.h new file mode 100755 index 000000000..249af6b99 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/rtc.h @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiTC_SYNC_STATE + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include +#include + +// algorithm state +#define RTC_SYNC_STATE 0 +#define RTC_NORMAL_STATE 1 +#define ROUNDS_IN_SYNC_BEFORE_SWITCH 3 + +// packet constants +#define INIT_PACKET_SIZE 1300 // bytes +#define HICN_PACKET_HEADER_SIZE 60 // bytes ipv6+tcp +#define NACK_HEADER_SIZE 8 // bytes +#define TIMESTAMP_SIZE 8 // bytes +#define RTC_INTEREST_LIFETIME 1000 // ms + +// controller constant +#define ROUND_LEN \ + 200 // ms interval of time on which we take decisions / measurements +#define MAX_RTX 128 +#define MIN_RTT_WIN 30 // rounds + +// cwin +#define INITIAL_CWIN 1 // packets +#define INITIAL_CWIN_MAX 100000 // packets +#define MIN_CWIN 5 // packets + +// statistics constants +#define BANDWIDTH_SLACK_FACTOR 1.5 +#define ESTIMATED_BW_ALPHA 0.7 +#define ESTIMATED_PACKET_SIZE 0.7 +#define ESTIMATED_LOSSES_ALPHA 0.8 +#define INTEREST_LIFETIME_REDUCTION_FACTOR 0.8 + +//#define MAX_LOSS_RATE 0.05 +//#define MAX_QUEUING_DELAY 200 //ms + +// cwin +#define INITIAL_CWIN 1 +#define MIN_CWIN 5 +#define WIN_DECREASE_FACTOR 0.8 +#define WIN_INCREASE_FACTOR 1.1 + +// protocol state +//#define RTC_CONGESTED_STATE 10 +//#define RTC_LOSSY_STATE 20 +//#define RTC_DELAY_STATE 30 +//#define RTC_NORMAL_STATE 40 + +// other constants +#define NANO_IN_A_SEC 1000000000 +#define MICRO_IN_A_SEC 1000000 +#define MILLI_IN_A_SEC 1000 + +// RTCP +#define MASK_RTCP_VERSION 192 +#define MASK_TYPE_CODE \ + 31 // this is RC in the RR/SR packet or FMT int the early feedback packets +#define RTPC_NACK_HEADER 12 // bytes +#define MAX_RTCP_SEQ_NUMBER 0xffff +#define RTCP_VERSION 2 +// RTCP TYPES +#define RTCP_SR 200 +#define RTCP_RR 201 +#define RTCP_SDES 202 +#define RTCP_RTPFB 205 +#define RTCP_PSFB 206 +// RTCP RC/FMT +#define RTCP_SDES_CNAME 1 +#define RTCP_RTPFB_GENERIC_NACK 1 +#define RTCP_PSFB_PLI 1 + +namespace transport { + +namespace protocol { + +struct sentInterest { + uint64_t transmissionTime; + uint8_t retransmissions; +}; + +class RTCTransportProtocol : public TransportProtocol { + public: + RTCTransportProtocol(interface::BaseSocket *icnet_socket); + + ~RTCTransportProtocol(); + + void start(utils::SharableVector &content_buffer); + + void stop(); + + void resume(); + + void onRTCPPacket(uint8_t *packet, size_t len); + + private: + // algo functions + void reset(); + void checkRound(); + + // CC functions + void updateDelayStats(const ContentObject &content_object); + void updateStats(uint32_t round_duration); + void updateCCState(); + void computeMaxWindow(uint32_t productionRate, uint32_t BDPWin); + void updateWindow(); + void decreaseWindow(); + void increaseWindow(); + void resetPreviousWindow(); + + // packet functions + void sendInterest(); + void scheduleNextInterest(); + void scheduleAppNackRtx(std::vector &nacks); + void onTimeout(Interest::Ptr &&interest); + void onNack(const ContentObject &content_object); + void onContentObject(Interest::Ptr &&interest, + ContentObject::Ptr &&content_object); + void returnContentToUser(const ContentObject &content_object); + + // RTCP functions + uint32_t hICN2RTP(uint32_t hicn_seq); + uint32_t RTP2hICN(uint32_t rtp_seq); + void processRtcpHeader(uint8_t *offset); + void errorParsingRtcpHeader(uint8_t *offset); + void processSDES(uint8_t *offset); + void processGenericNack(uint8_t *offset); + void processPli(uint8_t *offset); + + // controller var + std::chrono::steady_clock::time_point lastRoundBegin_; + // bool allPacketsInSync_; + // unsigned numberOfRoundsInSync_; + // unsigned numberOfCatchUpRounds_; + // bool catchUpPhase_; + unsigned currentState_; + + // uint32_t inProduction_; + + // cwin var + uint32_t currentCWin_; + uint32_t maxCWin_; + // uint32_t previousCWin_; + + // names/packets var + uint32_t actualSegment_; + int32_t RTPhICN_offset_; + uint32_t inflightInterestsCount_; + std::queue interestRetransmissions_; + std::vector inflightInterests_; + uint32_t nackedByProducerMaxSize_; + std::set + nackedByProducer_; // this is used to avoid retransmissions from the + // application for pakets for which we already got a + // past NACK by the producer these packet are too old, + // they will never be retrived + std::shared_ptr> content_buffer_; + uint32_t modMask_; + + // stats + uint32_t receivedBytes_; + uint32_t sentInterest_; + uint32_t receivedData_; + uint32_t packetLost_; + double avgPacketSize_; + bool gotNack_; + uint32_t gotFutureNack_; + uint32_t roundsWithoutNacks_; + uint32_t producerPathLabel_; // XXX we pick only one path lable for the + // producer for now, assuming the usage of a + // single path this should be extended to a + // vector + std::unordered_map> pathTable_; + uint32_t roundCounter_; + // std::vector minRTTwin_; + uint64_t minRtt_; + + std::unordered_map holes_; + uint32_t lastReceived_; + + // CC var + double estimatedBw_; + double lossRate_; + double queuingDelay_; + unsigned protocolState_; +}; + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/hicn/transport/protocols/rtc_data_path.cc b/libtransport/src/hicn/transport/protocols/rtc_data_path.cc new file mode 100755 index 000000000..6c9605fb2 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/rtc_data_path.cc @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +namespace transport { + +namespace protocol { + +RTCDataPath::RTCDataPath() + : min_rtt(UINT_MAX), + prev_min_rtt(UINT_MAX), + min_owd(INT_MAX), // this is computed like in LEDBAT, so it is not the + // real OWD, but the measured one, that depends on the + // clock of sender and receiver. the only meaningful + // value is is the queueing delay. for this reason we + // keep both RTT (for the windowd calculation) and OWD + // (for congestion/quality control) + prev_min_owd(INT_MAX), + avg_owd(0.0), + queuing_delay(0.0), + RTThistory_(HISTORY_LEN), + OWDhistory_(HISTORY_LEN){}; + +void RTCDataPath::insertRttSample(uint64_t rtt) { + // for the rtt we only keep track of the min one + if (rtt < min_rtt) min_rtt = rtt; +} + +void RTCDataPath::insertOwdSample(int64_t owd) { + // for owd we use both min and avg + if (owd < min_owd) min_owd = owd; + + avg_owd = (avg_owd * (1 - ALPHA_RTC)) + (owd * ALPHA_RTC); +} + +void RTCDataPath::roundEnd() { + // compute queuing delay + queuing_delay = avg_owd - getMinOwd(); + + // reset min_rtt and add it to the history + if (min_rtt != UINT_MAX) { + prev_min_rtt = min_rtt; + } else { + // this may happen if we do not receive any packet + // from this path in the last round. in this case + // we use the measure from the previuos round + min_rtt = prev_min_rtt; + } + + RTThistory_.pushBack(min_rtt); + min_rtt = UINT_MAX; + + // do the same for min owd + if (min_owd != INT_MAX) { + prev_min_owd = min_owd; + } else { + min_owd = prev_min_owd; + } + + OWDhistory_.pushBack(min_owd); + min_owd = INT_MAX; +} + +double RTCDataPath::getQueuingDealy() { return queuing_delay; } + +uint64_t RTCDataPath::getMinRtt() { return RTThistory_.begin(); } + +int64_t RTCDataPath::getMinOwd() { return OWDhistory_.begin(); } + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/rtc_data_path.h b/libtransport/src/hicn/transport/protocols/rtc_data_path.h new file mode 100755 index 000000000..ace16ff12 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/rtc_data_path.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#define ALPHA_RTC 0.125 +#define HISTORY_LEN 30 + +namespace transport { + +namespace protocol { + +class RTCDataPath { + public: + RTCDataPath(); + + public: + void insertRttSample(uint64_t rtt); + void insertOwdSample(int64_t owd); + + uint64_t getMinRtt(); + + double getQueuingDealy(); + + void roundEnd(); + + private: + int64_t getMinOwd(); + + uint64_t min_rtt; + uint64_t prev_min_rtt; + + int64_t min_owd; + int64_t prev_min_owd; + + double avg_owd; + + double queuing_delay; + + utils::MinFilter RTThistory_; + utils::MinFilter OWDhistory_; +}; + +} // namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/test/CMakeLists.txt b/libtransport/src/hicn/transport/protocols/test/CMakeLists.txt new file mode 100755 index 000000000..6f9fdb9aa --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/test/CMakeLists.txt @@ -0,0 +1,10 @@ +# Enable gcov output for the tests +add_definitions(--coverage) +set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage") + +set(TestsExpectedToPass + test_transport_producer) + +foreach(test ${TestsExpectedToPass}) + AddTest(${test}) +endforeach() \ No newline at end of file diff --git a/libtransport/src/hicn/transport/protocols/test/test_transport_producer.cc b/libtransport/src/hicn/transport/protocols/test/test_transport_producer.cc new file mode 100755 index 000000000..204f2cbe2 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/test/test_transport_producer.cc @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "../socket_producer.h" +#include "literals.h" + +#include +#include + +namespace transport { + +namespace protocol { + +namespace { +// The fixture for testing class Foo. +class ProducerTest : public ::testing::Test { + protected: + ProducerTest() : name_("b001::123|321"), producer_(io_service_) { + // You can do set-up work for each test here. + } + + virtual ~ProducerTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() { + // Code here will be called immediately after the constructor (right + // before each test). + } + + virtual void TearDown() { + // Code here will be called immediately after each test (right + // before the destructor). + } + + Name name_; + asio::io_service io_service_; + ProducerSocket producer_; +}; + +} // namespace + +// Tests that the Foo::Bar() method does Abc. +TEST_F(ProducerTest, ProduceContent) { + std::string content(250000, '?'); + + producer_.registerPrefix(Prefix("b001::/64")); + producer_.produce(name_, reinterpret_cast(content.data()), + content.size(), true); + producer_.setSocketOption(GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME, + 500000000_U32); + producer_.attach(); + producer_.serveForever(); +} + +} // namespace protocol + +} // namespace transport + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/libtransport/src/hicn/transport/protocols/vegas.cc b/libtransport/src/hicn/transport/protocols/vegas.cc new file mode 100755 index 000000000..b6d79bfcc --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/vegas.cc @@ -0,0 +1,630 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include + +namespace transport { + +namespace protocol { + +using namespace interface; + +VegasTransportProtocol::VegasTransportProtocol(BaseSocket *icnet_socket) + : TransportProtocol(icnet_socket), + is_final_block_number_discovered_(false), + final_block_number_(std::numeric_limits::max()), + last_reassembled_segment_(0), + content_buffer_size_(0), + current_window_size_(default_values::min_window_size), + interests_in_flight_(0), + next_suffix_(0), + interest_retransmissions_(1 << default_values::log_2_default_buffer_size), + interest_timepoints_(1 << default_values::log_2_default_buffer_size), + retx_count_(0), + receive_buffer_(1 << default_values::log_2_default_buffer_size), + unverified_segments_(1 << default_values::log_2_default_buffer_size), + verified_manifests_(1 << default_values::log_2_default_buffer_size), + mask_((1 << default_values::log_2_default_buffer_size) - 1), + incremental_suffix_index_(0), + suffix_queue_completed_(false), + download_with_manifest_(false), + next_manifest_interval_(0_U16), + interest_tx_(0), + interest_count_(0), + byte_count_(0), + average_rtt_(0.0) { + portal_ = socket_->portal_; + incremental_suffix_index_++; +} + +VegasTransportProtocol::~VegasTransportProtocol() { stop(); } + +void VegasTransportProtocol::reset() { + portal_->setConsumerCallback(this); + + is_final_block_number_discovered_ = false; + interest_pool_index_ = 0; + final_block_number_ = std::numeric_limits::max(); + next_suffix_ = 0; + interests_in_flight_ = 0; + last_reassembled_segment_ = 0; + content_buffer_size_ = 0; + content_buffer_->clear(); + interest_retransmissions_.clear(); + interest_retransmissions_.resize( + 1 << default_values::log_2_default_buffer_size, 0); + interest_timepoints_.clear(); + interest_timepoints_.resize(1 << default_values::log_2_default_buffer_size, + std::chrono::steady_clock::time_point()); + receive_buffer_.clear(); + unverified_segments_.clear(); + verified_manifests_.clear(); + next_manifest_interval_ = 0; + next_manifest_ = 0; + download_with_manifest_ = false; + incremental_suffix_index_ = 0; + + interest_tx_ = 0; + interest_count_ = 0; + byte_count_ = 0; + average_rtt_ = 0; + + // asio::io_service &io_service = portal_->getIoService(); + + // if (io_service.stopped()) { + // io_service.reset(); + // } +} + +void VegasTransportProtocol::start( + utils::SharableVector &content_buffer) { + + if(is_running_) + return; + + socket_->t0_ = std::chrono::steady_clock::now(); + + is_running_ = true; + content_buffer_ = content_buffer.shared_from_this(); + + reset(); + + sendInterest(next_suffix_++); + portal_->runEventsLoop(); + removeAllPendingInterests(); + is_running_ = false; + +} + +void VegasTransportProtocol::resume(){ + if(is_running_) + return; + + is_running_ = true; + sendInterest(next_suffix_++); + portal_->runEventsLoop(); + removeAllPendingInterests(); + is_running_ = false; +} + +void VegasTransportProtocol::sendInterest(std::uint64_t next_suffix) { + auto interest = getInterest(); + socket_->network_name_.setSuffix(next_suffix); + interest->setName(socket_->network_name_); + + interest->setLifetime(uint32_t(socket_->interest_lifetime_)); + + if (socket_->on_interest_output_ != VOID_HANDLER) { + socket_->on_interest_output_(*socket_, *interest); + } + + if (TRANSPORT_EXPECT_FALSE(!is_running_)) { + return; + } + + interests_in_flight_++; + interest_retransmissions_[next_suffix & mask_] = 0; + interest_timepoints_[next_suffix & mask_] = std::chrono::steady_clock::now(); + + using namespace std::placeholders; + portal_->sendInterest(std::move(interest)); +} + +void VegasTransportProtocol::stop() { + is_running_ = false; + portal_->stopEventsLoop(); +} + +void VegasTransportProtocol::onContentSegment( + Interest::Ptr &&interest, ContentObject::Ptr &&content_object) { + uint32_t incremental_suffix = content_object->getName().getSuffix(); + bool virtual_download = socket_->virtual_download_; + + if (verifyContentObject(*content_object)) { + byte_count_ += content_object->getPayload().length(); + + if (TRANSPORT_EXPECT_FALSE(content_object->testRst())) { + is_final_block_number_discovered_ = true; + final_block_number_ = incremental_suffix; + } + + if (!virtual_download) { + receive_buffer_.emplace( + std::make_pair(incremental_suffix, std::move(content_object))); + reassemble(); + } else if (TRANSPORT_EXPECT_FALSE(is_final_block_number_discovered_ && + incremental_suffix == + final_block_number_)) { + returnContentToUser(); + } + } else { + unverified_segments_.emplace( + std::make_pair(incremental_suffix, std::move(content_object))); + } +} + +void VegasTransportProtocol::afterContentReception( + const Interest &interest, const ContentObject &content_object) { + increaseWindow(); +} + +void VegasTransportProtocol::afterDataUnsatisfied(uint64_t segment) { + decreaseWindow(); +} + +void VegasTransportProtocol::scheduleNextInterests() { + if (is_running_) { + uint32_t next_suffix; + while (interests_in_flight_ < current_window_size_) { + if (download_with_manifest_) { + if (suffix_queue_.size() * 2 < current_window_size_ && + next_manifest_ < final_block_number_ && next_manifest_interval_) { + next_manifest_ += next_manifest_interval_; + sendInterest(next_manifest_); + continue; + } + + if (suffix_queue_.pop(next_suffix)) { + // next_suffix = suffix_queue_.front(); + sendInterest(next_suffix); + // suffix_queue_.pop_front(); + } else { + if (!suffix_queue_completed_) { + TRANSPORT_LOGE("Empty queue!!!!!!"); + } + break; + } + } else { + if (is_final_block_number_discovered_) { + if (next_suffix_ > final_block_number_) { + return; + } + } + + sendInterest(next_suffix_++); + } + } + } +} + +void VegasTransportProtocol::decreaseWindow() { + if (current_window_size_ > socket_->min_window_size_) { + current_window_size_ = std::ceil(current_window_size_ / 2); + socket_->current_window_size_ = current_window_size_; + } +} + +void VegasTransportProtocol::increaseWindow() { + if (current_window_size_ < socket_->max_window_size_) { + current_window_size_++; + socket_->max_window_size_ = current_window_size_; + } +}; + +void VegasTransportProtocol::changeInterestLifetime(uint64_t segment) { + std::chrono::steady_clock::duration duration = + std::chrono::steady_clock::now() - interest_timepoints_[segment]; + rtt_estimator_.addMeasurement( + std::chrono::duration_cast(duration)); + + RtoEstimator::Duration rto = rtt_estimator_.computeRto(); + std::chrono::milliseconds lifetime = + std::chrono::duration_cast(rto); + + socket_->interest_lifetime_ = lifetime.count(); +} + +void VegasTransportProtocol::returnContentToUser() { + if (socket_->on_payload_retrieved_ != VOID_HANDLER) { + socket_->on_payload_retrieved_(*socket_, byte_count_, + std::make_error_code(std::errc(0))); + } + + stop(); +} + +void VegasTransportProtocol::onManifest( + std::unique_ptr &&manifest) { + if (TRANSPORT_EXPECT_FALSE(!is_running_)) { + return; + } + + download_with_manifest_ = true; + + uint32_t segment = manifest->getName().getSuffix(); + + if (verifyManifest(*manifest)) { + manifest->decode(); + + if (TRANSPORT_EXPECT_TRUE(manifest->getVersion() == + core::ManifestVersion::VERSION_1)) { + switch (manifest->getManifestType()) { + case core::ManifestType::INLINE_MANIFEST: { + auto _it = manifest->getSuffixList().begin(); + auto _end = --manifest->getSuffixList().end(); + + if (TRANSPORT_EXPECT_FALSE(manifest->isFinalManifest())) { + _end++; + } + + // Get final block number + is_final_block_number_discovered_ = true; + final_block_number_ = manifest->getFinalBlockNumber(); + + for (; _it != _end; _it++) { + suffix_hash_map_[_it->first] = std::make_pair( + std::vector(_it->second, _it->second + 32), + manifest->getHashAlgorithm()); + suffix_queue_.push(_it->first); + } + + next_manifest_interval_ = manifest->getSuffixList().size(); + + if (manifest->isFinalManifest()) { + suffix_queue_completed_ = true; + // Give it a try + if (verifier_thread_) { + asio::io_service &io_service = portal_->getIoService(); + io_service.post([this]() { scheduleNextInterests(); }); + } + } + + break; + } + case core::ManifestType::FLIC_MANIFEST: { + throw errors::NotImplementedException(); + } + case core::ManifestType::FINAL_CHUNK_NUMBER: { + throw errors::NotImplementedException(); + } + } + } + + if (!socket_->virtual_download_) { + receive_buffer_.emplace( + std::make_pair(segment, std::move(manifest->getPacket()))); + reassemble(); + } else { + if (segment >= final_block_number_) { + stop(); + } + } + } +} + +bool VegasTransportProtocol::verifyManifest( + const ContentObjectManifest &manifest) { + if (!socket_->verify_signature_) { + return true; + } + + bool is_data_secure = false; + + if (socket_->on_content_object_verification_ == VOID_HANDLER) { + is_data_secure = static_cast(socket_->verifier_.verify(manifest)); + } else if (socket_->on_content_object_verification_(*socket_, manifest)) { + is_data_secure = true; + } + + if (TRANSPORT_EXPECT_FALSE(!is_data_secure)) { + TRANSPORT_LOGE("Verification failed for %s\n", + manifest.getName().toString().c_str()); + } + + return is_data_secure; +} + +// TODO Add the name in the digest computation! +void VegasTransportProtocol::onContentObject( + Interest::Ptr &&interest, ContentObject::Ptr &&content_object) { + uint32_t incremental_suffix = content_object->getName().getSuffix(); + + std::chrono::microseconds rtt; + Time now = std::chrono::steady_clock::now(); + std::chrono::steady_clock::duration duration = + now - interest_timepoints_[incremental_suffix & mask_]; + rtt = std::chrono::duration_cast(duration); + + average_rtt_ = (0.7 * average_rtt_) + (0.3 * (double)rtt.count()); + + if (socket_->on_timer_expires_ != VOID_HANDLER) { + auto dt = std::chrono::duration_cast(now - socket_->t0_); + if (dt.count() > socket_->timer_interval_milliseconds_) { + socket_->on_timer_expires_(*socket_, byte_count_, dt, + current_window_size_, retx_count_, + std::round(average_rtt_)); + socket_->t0_ = std::chrono::steady_clock::now(); + } + } + + interests_in_flight_--; + + if (TRANSPORT_EXPECT_FALSE(!is_running_ || incremental_suffix == ~0_U64 || + receive_buffer_.find(incremental_suffix) != + receive_buffer_.end())) { + return; + } + + changeInterestLifetime(incremental_suffix); + + if (socket_->on_content_object_input_ != VOID_HANDLER) { + socket_->on_content_object_input_(*socket_, *content_object); + } + + if (socket_->on_interest_satisfied_ != VOID_HANDLER) { + socket_->on_interest_satisfied_(*socket_, *interest); + } + + if (!interest_retransmissions_[incremental_suffix & mask_]) { + afterContentReception(*interest, *content_object); + } + + if (TRANSPORT_EXPECT_FALSE(content_object->getPayloadType() == + PayloadType::MANIFEST)) { + // TODO Fix manifest!! + auto manifest = + std::make_unique(std::move(content_object)); + + if (verifier_thread_ && incremental_suffix != 0) { + // verifier_thread_->add(std::bind(&VegasTransportProtocol::onManifest, + // this, std::move(manifest))); + } else { + onManifest(std::move(manifest)); + } + } else if (content_object->getPayloadType() == PayloadType::CONTENT_OBJECT) { + if (verifier_thread_) { + // verifier_thread_->add(std::bind(&VegasTransportProtocol::onContentSegment, + // this, std::move(content_object))); + } else { + onContentSegment(std::move(interest), std::move(content_object)); + } + } + + scheduleNextInterests(); +} + +bool VegasTransportProtocol::verifyContentObject( + const ContentObject &content_object) { + if (!dynamic_cast(socket_)->verify_signature_) { + return true; + } + + uint64_t segment = content_object.getName().getSuffix(); + + bool ret = false; + + if (download_with_manifest_) { + auto it = suffix_hash_map_.find(segment); + if (it != suffix_hash_map_.end()) { + auto hash_type = static_cast(it->second.second); + auto data_packet_digest = content_object.computeDigest(it->second.second); + auto data_packet_digest_bytes = + data_packet_digest.getDigest().data(); + std::vector &manifest_digest_bytes = it->second.first; + + if (utils::CryptoHash::compareBinaryDigest(data_packet_digest_bytes, + manifest_digest_bytes.data(), + hash_type)) { + suffix_hash_map_.erase(it); + ret = true; + } else { + throw errors::RuntimeException( + "Verification failure policy has to be implemented."); + } + } + } else { + ret = static_cast( + dynamic_cast(socket_)->verifier_.verify( + content_object)); + + if (!ret) { + throw errors::RuntimeException( + "Verification failure policy has to be implemented."); + } + } + + return ret; + ; +} + +void VegasTransportProtocol::onTimeout(Interest::Ptr &&interest) { + TRANSPORT_LOGW("Timeout on %s", interest->getName().toString().c_str()); + + if (TRANSPORT_EXPECT_FALSE(!is_running_)) { + return; + } + + interests_in_flight_--; + + uint64_t segment = interest->getName().getSuffix(); + + // Do not retransmit interests asking contents that do not exist. + if (is_final_block_number_discovered_) { + if (segment > final_block_number_) { + return; + } + } + + if (socket_->on_interest_timeout_ != VOID_HANDLER) { + socket_->on_interest_timeout_(*socket_, *interest); + } + + afterDataUnsatisfied(segment); + + if (TRANSPORT_EXPECT_TRUE(interest_retransmissions_[segment & mask_] < + socket_->max_retransmissions_)) { + retx_count_++; + + if (socket_->on_interest_retransmission_ != VOID_HANDLER) { + socket_->on_interest_retransmission_(*socket_, *interest); + } + + if (socket_->on_interest_output_ != VOID_HANDLER) { + socket_->on_interest_output_(*socket_, *interest); + } + + if (!is_running_) { + return; + } + + // retransmit + interests_in_flight_++; + interest_retransmissions_[segment & mask_]++; + + using namespace std::placeholders; + portal_->sendInterest(std::move(interest)); + } else { + TRANSPORT_LOGE("Stop: reached max retx limit."); + partialDownload(); + stop(); + } +} + +void VegasTransportProtocol::copyContent(const ContentObject &content_object) { + Array a = content_object.getPayload(); + + content_buffer_->insert(content_buffer_->end(), (uint8_t *)a.data(), + (uint8_t *)a.data() + a.length()); + + bool download_completed = + is_final_block_number_discovered_ && + content_object.getName().getSuffix() == final_block_number_; + + if (TRANSPORT_EXPECT_FALSE(download_completed || !is_running_)) { + // asio::io_service& io_service = portal_->getIoService(); + // io_service.post([this] () { + returnContentToUser(); + // }); + } +} + +void VegasTransportProtocol::reassemble() { + uint64_t index = last_reassembled_segment_; + auto it = receive_buffer_.find(index); + + do { + if (it->second->getPayloadType() == PayloadType::CONTENT_OBJECT) { + copyContent(*it->second); + receive_buffer_.erase(it); + } + + index = ++last_reassembled_segment_; + it = receive_buffer_.find(index); + } while (it != receive_buffer_.end()); +} + +void VegasTransportProtocol::partialDownload() { + if (!socket_->virtual_download_) { + reassemble(); + } + + if (socket_->on_payload_retrieved_ != VOID_HANDLER) { + socket_->on_payload_retrieved_( + *socket_, byte_count_, + std::make_error_code(std::errc(std::errc::io_error))); + } +} + +// TODO Check vegas protocol +// void VegasTransportProtocol::checkForFastRetransmission(const Interest +// &interest) { +// uint64_t segNumber = interest.getName().getSuffix(); +// received_segments_[segNumber] = true; +// fast_retransmitted_segments.erase(segNumber); + +// uint64_t possibly_lost_segment = 0; +// uint64_t highest_received_segment = received_segments_.rbegin()->first; + +// for (uint64_t i = 0; i <= highest_received_segment; i++) { +// if (received_segments_.find(i) == received_segments_.end()) { +// if (fast_retransmitted_segments.find(i) == +// fast_retransmitted_segments.end()) { +// possibly_lost_segment = i; +// uint8_t out_of_order_segments = 0; +// for (uint64_t j = i; j <= highest_received_segment; j++) { +// if (received_segments_.find(j) != received_segments_.end()) { +// out_of_order_segments++; +// if (out_of_order_segments >= +// default_values::max_out_of_order_segments) { +// fast_retransmitted_segments[possibly_lost_segment] = true; +// fastRetransmit(interest, possibly_lost_segment); +// } +// } +// } +// } +// } +// } +// } + +// void VegasTransportProtocol::fastRetransmit(const Interest &interest, +// uint32_t chunk_number) { +// if (interest_retransmissions_[chunk_number & mask_] < +// socket_->max_retransmissions_) { +// Name name = interest.getName(); +// name.setSuffix(chunk_number); + +// std::shared_ptr retx_interest = +// std::make_shared(name); + +// if (socket_->on_interest_retransmission_ != VOID_HANDLER) { +// socket_->on_interest_retransmission_(*socket_, *retx_interest); +// } + +// if (socket_->on_interest_output_ != VOID_HANDLER) { +// socket_->on_interest_output_(*socket_, *retx_interest); +// } + +// if (!is_running_) { +// return; +// } + +// interests_in_flight_++; +// interest_retransmissions_[chunk_number & mask_]++; + +// using namespace std::placeholders; +// portal_->sendInterest(std::move(retx_interest)); +// } +// } + +void VegasTransportProtocol::removeAllPendingInterests() { portal_->clear(); } + +} // end namespace protocol + +} // namespace transport diff --git a/libtransport/src/hicn/transport/protocols/vegas.h b/libtransport/src/hicn/transport/protocols/vegas.h new file mode 100755 index 000000000..7791ffc94 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/vegas.h @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace transport { + +namespace protocol { + +typedef utils::CircularFifo SuffixQueue; +typedef std::chrono::time_point Time; +typedef std::chrono::milliseconds TimeDuration; + +class VegasTransportProtocol : public TransportProtocol { + public: + VegasTransportProtocol(interface::BaseSocket *icnet_socket); + + virtual ~VegasTransportProtocol(); + + virtual void start(utils::SharableVector &content_buffer) override; + + void stop() override; + + void resume() override; + + protected: + void reset(); + + void sendInterest(std::uint64_t next_suffix); + + void onContentSegment(Interest::Ptr &&interest, + ContentObject::Ptr &&content_object); + + bool verifyContentObject(const ContentObject &content_object); + + bool verifyManifest(const interface::ContentObjectManifest &manifest); + + virtual void onTimeout(Interest::Ptr &&interest) override; + + void onManifest(std::unique_ptr &&manifest); + + void onContentObject(Interest::Ptr &&interest, + ContentObject::Ptr &&content_object) override; + + virtual void changeInterestLifetime(uint64_t segment); + + void scheduleNextInterests(); + + virtual void decreaseWindow(); + + virtual void increaseWindow(); + + virtual void afterContentReception(const Interest &interest, + const ContentObject &content_object); + + virtual void afterDataUnsatisfied(uint64_t segment); + + void reassemble(); + + void returnContentToUser(); + + void partialDownload(); + + virtual void copyContent(const ContentObject &content_object); + + // virtual void checkForFastRetransmission(const Interest &interest); + + // void fastRetransmit(const Interest &interest, uint32_t chunk_number); + + void removeAllPendingInterests(); + + protected: + void handleTimeout(const std::error_code &ec); + + // reassembly variables + volatile bool is_final_block_number_discovered_; + std::atomic final_block_number_; + uint64_t last_reassembled_segment_; + std::shared_ptr> content_buffer_; + size_t content_buffer_size_; + + // transmission variablesis_final_block_number_discovered_ + double current_window_size_; + double pending_window_size_; + uint64_t interests_in_flight_; + uint64_t next_suffix_; + std::vector interest_retransmissions_; + std::vector interest_timepoints_; + RtoEstimator rtt_estimator_; + + uint32_t retx_count_; + + // buffers + std::unordered_map + receive_buffer_; // verified segments by segment number + std::unordered_map + unverified_segments_; // used with embedded manifests + std::unordered_map + verified_manifests_; // by segment number + + std::uint16_t interest_pool_index_; + std::uint16_t mask_; + + // suffix randomization: since the suffixes in the manifests could not be in a + // sequential order, we need to map those suffixes into an ordered sequence. + std::unordered_map + incremental_suffix_to_real_suffix_map_; + std::unordered_map + real_suffix_to_incremental_suffix_map_; + std::uint32_t incremental_suffix_index_; + + // verification + std::unordered_map, HashAlgorithm>> + suffix_hash_map_; + + // Fast Retransmission + std::map received_segments_; + std::unordered_map fast_retransmitted_segments; + + // Suffix queue + volatile bool suffix_queue_completed_; + SuffixQueue suffix_queue_; + + volatile bool download_with_manifest_; + uint32_t next_manifest_; + std::atomic next_manifest_interval_; + + std::unique_ptr verifier_thread_; + + uint32_t interest_tx_; + uint32_t interest_count_; + + uint64_t byte_count_; + double average_rtt_; + + std::unordered_map sign_time_; +}; + +} // namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/vegas_rto_estimator.cc b/libtransport/src/hicn/transport/protocols/vegas_rto_estimator.cc new file mode 100755 index 000000000..f5f797bbe --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/vegas_rto_estimator.cc @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +namespace transport { + +namespace protocol { + +using namespace interface; + +RtoEstimator::RtoEstimator(Duration min_rto) + : smoothed_rtt_(RtoEstimator::getInitialRtt().count()), + rtt_variation_(0), + first_measurement_(true), + last_rto_(min_rto.count()) {} + +void RtoEstimator::addMeasurement(Duration rtt) { + double duration = static_cast(rtt.count()); + if (first_measurement_) { + smoothed_rtt_ = duration; + rtt_variation_ = duration / 2; + first_measurement_ = false; + } else { + rtt_variation_ = (1 - default_values::beta) * rtt_variation_ + + default_values::beta * std::abs(smoothed_rtt_ - duration); + smoothed_rtt_ = (1 - default_values::alpha) * smoothed_rtt_ + + default_values::alpha * duration; + } +} + +RtoEstimator::Duration RtoEstimator::computeRto() const { + double rto = smoothed_rtt_ + + std::max(double(default_values::clock_granularity.count()), + default_values::k* rtt_variation_); + return Duration(static_cast(rto)); +} + +} // end namespace protocol + +} // end namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/protocols/vegas_rto_estimator.h b/libtransport/src/hicn/transport/protocols/vegas_rto_estimator.h new file mode 100755 index 000000000..e84afc49c --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/vegas_rto_estimator.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +// Implementation inspired from RFC6298 +// (https://tools.ietf.org/search/rfc6298#ref-JK88) + +namespace transport { + +namespace protocol { + +class RtoEstimator { + public: + typedef std::chrono::microseconds Duration; + + static Duration getInitialRtt() { return std::chrono::seconds(1); } + + RtoEstimator(Duration min_rto = std::chrono::seconds(1)); + + void addMeasurement(Duration measure); + + Duration computeRto() const; + + private: + double smoothed_rtt_; + double rtt_variation_; + bool first_measurement_; + double last_rto_; +}; + +} // end namespace protocol + +} // end namespace transport \ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/CMakeLists.txt b/libtransport/src/hicn/transport/utils/CMakeLists.txt new file mode 100755 index 000000000..088fb5862 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/CMakeLists.txt @@ -0,0 +1,76 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/string_tokenizer.cc + ${CMAKE_CURRENT_SOURCE_DIR}/uri.cc + ${CMAKE_CURRENT_SOURCE_DIR}/daemonizator.cc + ${CMAKE_CURRENT_SOURCE_DIR}/min_filter.h + ${CMAKE_CURRENT_SOURCE_DIR}/signer.cc + ${CMAKE_CURRENT_SOURCE_DIR}/verifier.cc + ${CMAKE_CURRENT_SOURCE_DIR}/identity.cc + ${CMAKE_CURRENT_SOURCE_DIR}/log.cc + ${CMAKE_CURRENT_SOURCE_DIR}/membuf.cc + ${CMAKE_CURRENT_SOURCE_DIR}/content_store.cc +) + + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/array.h + ${CMAKE_CURRENT_SOURCE_DIR}/string_tokenizer.h + ${CMAKE_CURRENT_SOURCE_DIR}/hash.h + ${CMAKE_CURRENT_SOURCE_DIR}/uri.h + ${CMAKE_CURRENT_SOURCE_DIR}/daemonizator.h + ${CMAKE_CURRENT_SOURCE_DIR}/sharable_vector.h + ${CMAKE_CURRENT_SOURCE_DIR}/branch_prediction.h + ${CMAKE_CURRENT_SOURCE_DIR}/event_reactor.h + ${CMAKE_CURRENT_SOURCE_DIR}/deadline_timer.h + ${CMAKE_CURRENT_SOURCE_DIR}/ring_buffer.h + ${CMAKE_CURRENT_SOURCE_DIR}/event_reactor.h + ${CMAKE_CURRENT_SOURCE_DIR}/min_filter.h + ${CMAKE_CURRENT_SOURCE_DIR}/stream_buffer.h + ${CMAKE_CURRENT_SOURCE_DIR}/endianess.h + ${CMAKE_CURRENT_SOURCE_DIR}/literals.h + ${CMAKE_CURRENT_SOURCE_DIR}/signer.h + ${CMAKE_CURRENT_SOURCE_DIR}/verifier.h + ${CMAKE_CURRENT_SOURCE_DIR}/crypto_hasher.h + ${CMAKE_CURRENT_SOURCE_DIR}/crypto_suite.h + ${CMAKE_CURRENT_SOURCE_DIR}/crypto_hash.h + ${CMAKE_CURRENT_SOURCE_DIR}/crypto_hash_type.h + ${CMAKE_CURRENT_SOURCE_DIR}/identity.h + ${CMAKE_CURRENT_SOURCE_DIR}/conversions.h + ${CMAKE_CURRENT_SOURCE_DIR}/linux.h + ${CMAKE_CURRENT_SOURCE_DIR}/log.h + ${CMAKE_CURRENT_SOURCE_DIR}/event_thread.h + ${CMAKE_CURRENT_SOURCE_DIR}/object_pool.h + ${CMAKE_CURRENT_SOURCE_DIR}/membuf.h + ${CMAKE_CURRENT_SOURCE_DIR}/spinlock.h + ${CMAKE_CURRENT_SOURCE_DIR}/content_store.h + ${CMAKE_CURRENT_SOURCE_DIR}/key_id.h +) + +if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/epoll_event_reactor.h + ${CMAKE_CURRENT_SOURCE_DIR}/fd_deadline_timer.h + ) + + list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/epoll_event_reactor.cc + ) +endif() + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) diff --git a/libtransport/src/hicn/transport/utils/array.h b/libtransport/src/hicn/transport/utils/array.h new file mode 100755 index 000000000..a3a66e498 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/array.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include + +namespace utils { + +template +class Array { + public: + explicit Array(const T *array, size_t size) : array_(array), size_(size) { + this->array_ = array; + this->size_ = size; + } + + Array() : array_(nullptr), size_(0) { + this->array_ = nullptr; + this->size_ = 0; + } + + TRANSPORT_ALWAYS_INLINE const T *data() const { return array_; } + + TRANSPORT_ALWAYS_INLINE T *writableData() const { + return const_cast(array_); + } + + TRANSPORT_ALWAYS_INLINE std::size_t length() const { return size_; } + + TRANSPORT_ALWAYS_INLINE Array &setData(const T *data) { + array_ = data; + return *this; + } + + TRANSPORT_ALWAYS_INLINE Array &setSize(std::size_t size) { + size_ = size; + return *this; + } + + TRANSPORT_ALWAYS_INLINE bool empty() { return !size_; } + + private: + const T *array_; + std::size_t size_; +}; + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/branch_prediction.h b/libtransport/src/hicn/transport/utils/branch_prediction.h new file mode 100755 index 000000000..b12282fe8 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/branch_prediction.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#undef TRANSPORT_EXPECT_TRUE +#undef TRANSPORT_EXPECT_FALSE + +#define TRANSPORT_EXPECT_TRUE(x) __builtin_expect((x), 1) +#define TRANSPORT_EXPECT_FALSE(x) __builtin_expect((x), 0) \ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/content_store.cc b/libtransport/src/hicn/transport/utils/content_store.cc new file mode 100755 index 000000000..4c7637dad --- /dev/null +++ b/libtransport/src/hicn/transport/utils/content_store.cc @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +namespace utils { + +ContentStore::ContentStore(std::size_t max_packets) + : max_content_store_size_(max_packets) {} + +ContentStore::~ContentStore() {} + +void ContentStore::insert( + const std::shared_ptr &content_object) { + if (max_content_store_size_ == 0) { + return; + } + + std::unique_lock lock(cs_mutex_); + + if (TRANSPORT_EXPECT_FALSE(content_store_hash_table_.size() != + lru_list_.size())) { + TRANSPORT_LOGW("Inconsistent size!!!!"); + TRANSPORT_LOGW("Hash Table: %zu |||| FIFO List: %zu", + content_store_hash_table_.size(), lru_list_.size()); + } + + // Check if the content can be cached + if (content_object->getLifetime() > 0) { + if (content_store_hash_table_.size() >= max_content_store_size_) { + content_store_hash_table_.erase(lru_list_.back()); + lru_list_.pop_back(); + } + + // Insert new item + + auto it = content_store_hash_table_.find(content_object->getName()); + if (it != content_store_hash_table_.end()) { + lru_list_.erase(it->second.second); + content_store_hash_table_.erase(content_object->getName()); + } + + lru_list_.push_front(std::cref(content_object->getName())); + auto pos = lru_list_.begin(); + content_store_hash_table_[content_object->getName()] = ContentStoreEntry( + ObjectTimeEntry(content_object, std::chrono::steady_clock::now()), pos); + } +} + +const std::shared_ptr &ContentStore::find( + const Interest &interest) { + std::unique_lock lock(cs_mutex_); + auto it = content_store_hash_table_.find(interest.getName()); + if (it != content_store_hash_table_.end()) { + // if (std::chrono::duration_cast( + // std::chrono::steady_clock::now() - it->second.first.second).count() + // < it->second.first.first->getLifetime() || + // it->second.first.first->getLifetime() == + // default_values::never_expire_time) { + return it->second.first.first; + // } + } + + return empty_reference_; +} + +void ContentStore::erase(const Name &exact_name) { + std::unique_lock lock(cs_mutex_); + auto it = content_store_hash_table_.find(exact_name); + lru_list_.erase(it->second.second); + content_store_hash_table_.erase(exact_name); +} + +void ContentStore::setLimit(size_t max_packets) { + max_content_store_size_ = max_packets; +} + +std::size_t ContentStore::getLimit() const { return max_content_store_size_; } + +std::size_t ContentStore::size() const { + return content_store_hash_table_.size(); +} + +void ContentStore::printContent() { + for (auto &item : content_store_hash_table_) { + if (item.second.first.first->getPayloadType() == + transport::core::PayloadType::MANIFEST) { + TRANSPORT_LOGI("Manifest: %s\n", + item.second.first.first->getName().toString().c_str()); + } + } +} + +} // end namespace utils \ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/content_store.h b/libtransport/src/hicn/transport/utils/content_store.h new file mode 100755 index 000000000..ab4963fff --- /dev/null +++ b/libtransport/src/hicn/transport/utils/content_store.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include + +namespace transport { + +namespace core { +class Name; +class ContentObject; +class Interest; +} // namespace core + +} // namespace transport + +namespace utils { + +using Name = transport::core::Name; +using ContentObject = transport::core::ContentObject; +using Interest = transport::core::Interest; + +typedef std::pair, + std::chrono::steady_clock::time_point> + ObjectTimeEntry; +typedef std::pair>::iterator> + ContentStoreEntry; +typedef std::list> LRUList; +typedef std::unordered_map ContentStoreHashTable; + +class ContentStore { + public: + explicit ContentStore(std::size_t max_packets = 65536); + + ~ContentStore(); + + void insert(const std::shared_ptr &content_object); + + const std::shared_ptr &find(const Interest &interest); + + void erase(const Name &exact_name); + + void setLimit(size_t max_packets); + + size_t getLimit() const; + + size_t size() const; + + void printContent(); + + private: + ContentStoreHashTable content_store_hash_table_; + LRUList lru_list_; + std::shared_ptr empty_reference_; + std::size_t max_content_store_size_; + std::mutex cs_mutex_; +}; + +} // end namespace utils \ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/conversions.h b/libtransport/src/hicn/transport/utils/conversions.h new file mode 100755 index 000000000..24b529206 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/conversions.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace utils { + +static TRANSPORT_ALWAYS_INLINE int convertStringToMacAddress( + const std::string& mac_address, uint8_t* mac_byte_array) { + const char* mac = mac_address.c_str(); + + sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mac_byte_array[0], + &mac_byte_array[1], &mac_byte_array[2], &mac_byte_array[3], + &mac_byte_array[4], &mac_byte_array[5]); + + return 0; +} + +} // namespace utils \ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/crypto_hash.h b/libtransport/src/hicn/transport/utils/crypto_hash.h new file mode 100755 index 000000000..0c15c8bda --- /dev/null +++ b/libtransport/src/hicn/transport/utils/crypto_hash.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +extern "C" { +#include +}; + +#include +#include + +namespace utils { + +class CryptoHasher; + +struct EnumClassHash { + template + std::size_t operator()(T t) const { + return static_cast(t); + } +}; + +static std::unordered_map + hash_size_map = {{CryptoHashType::SHA_256, 32}, + {CryptoHashType::CRC32C, 4}, + {CryptoHashType::SHA_512, 64}}; + +class Signer; +class Verifier; + +class CryptoHash { + friend class CryptoHasher; + friend class Signer; + friend class Verifier; + + public: + CryptoHash() : hash_(nullptr) {} + + CryptoHash(const CryptoHash& other) { + if (other.hash_) { + hash_ = parcCryptoHash_Acquire(other.hash_); + } + } + + CryptoHash(CryptoHash&& other) { + if (other.hash_) { + hash_ = parcCryptoHash_Acquire(other.hash_); + } + } + + template + CryptoHash(const T* buffer, std::size_t length, CryptoHashType hash_type) { + hash_ = parcCryptoHash_CreateFromArray( + static_cast(hash_type), buffer, length); + } + + ~CryptoHash() { + if (hash_) { + parcCryptoHash_Release(&hash_); + } + } + + CryptoHash& operator=(const CryptoHash& other) { + if (other.hash_) { + hash_ = parcCryptoHash_Acquire(other.hash_); + } + + return *this; + } + + template + utils::Array getDigest() const { + return utils::Array( + static_cast(parcBuffer_Overlay(parcCryptoHash_GetDigest(hash_), 0)), + parcBuffer_Remaining(parcCryptoHash_GetDigest(hash_))); + } + + CryptoHashType getType() { + return static_cast(parcCryptoHash_GetDigestType(hash_)); + } + + template + static bool compareBinaryDigest(const T* digest1, const T* digest2, + CryptoHashType hash_type) { + if (hash_size_map.find(hash_type) == hash_size_map.end()) { + return false; + } + + return !static_cast( + std::memcmp(digest1, digest2, hash_size_map[hash_type])); + } + + private: + PARCCryptoHash* hash_; +}; + +} // namespace utils \ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/crypto_hash_type.h b/libtransport/src/hicn/transport/utils/crypto_hash_type.h new file mode 100755 index 000000000..b7597e208 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/crypto_hash_type.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +extern "C" { +#include +}; + +namespace utils { + +enum class CryptoHashType : uint8_t { + SHA_256 = PARCCryptoHashType_SHA256, + SHA_512 = PARCCryptoHashType_SHA512, + CRC32C = PARCCryptoHashType_CRC32C, + NULL_HASH = PARCCryptoHashType_NULL +}; + +} \ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/crypto_hasher.h b/libtransport/src/hicn/transport/utils/crypto_hasher.h new file mode 100755 index 000000000..c34a26fac --- /dev/null +++ b/libtransport/src/hicn/transport/utils/crypto_hasher.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +extern "C" { +#include +}; + +namespace utils { + +class CryptoHasher { + public: + CryptoHasher(CryptoHashType hash_type) + : hasher_(parcCryptoHasher_Create( + static_cast(hash_type))), + managed_(true) {} + + CryptoHasher(PARCCryptoHasher* hasher) : hasher_(hasher), managed_(false) {} + + ~CryptoHasher() { + if (managed_) { + parcCryptoHasher_Release(&hasher_); + } + } + + CryptoHasher& init() { + if (parcCryptoHasher_Init(hasher_) == -1) { + throw errors::RuntimeException("Cryptohash init failed."); + } + + return *this; + } + + template + CryptoHasher& updateBytes(const T* buffer, std::size_t length) { + if (parcCryptoHasher_UpdateBytes(hasher_, buffer, length) == -1) { + throw errors::RuntimeException("Cryptohash updateBytes failed."); + } + return *this; + } + + CryptoHash finalize() { + CryptoHash hash; + hash.hash_ = parcCryptoHasher_Finalize(hasher_); + return hash; + } + + private: + PARCCryptoHasher* hasher_; + bool managed_; +}; + +} // namespace utils \ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/crypto_suite.h b/libtransport/src/hicn/transport/utils/crypto_suite.h new file mode 100755 index 000000000..8ae32b846 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/crypto_suite.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +extern "C" { +#include +}; + +namespace utils { + +enum class CryptoSuite : uint8_t { + RSA_SHA256 = PARCCryptoSuite_RSA_SHA256, + DSA_SHA256 = PARCCryptoSuite_DSA_SHA256, + RSA_SHA512 = PARCCryptoSuite_RSA_SHA512, + HMAC_SHA256 = PARCCryptoSuite_HMAC_SHA256, + HMAC_SHA512 = PARCCryptoSuite_HMAC_SHA512, + NULL_CRC32C = PARCCryptoSuite_NULL_CRC32C, + ECDSA_256K1 = PARCCryptoSuite_ECDSA_SHA256, + UNKNOWN = PARCCryptoSuite_UNKNOWN +}; + +} diff --git a/libtransport/src/hicn/transport/utils/daemonizator.cc b/libtransport/src/hicn/transport/utils/daemonizator.cc new file mode 100755 index 000000000..d9b3109af --- /dev/null +++ b/libtransport/src/hicn/transport/utils/daemonizator.cc @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include + +namespace utils { + +void Daemonizator::daemonize(bool close_fds) { + pid_t process_id = 0; + pid_t sid = 0; + + // Create child process + process_id = fork(); + + // Indication of fork() failure + if (process_id < 0) { + throw errors::RuntimeException("Fork failed."); + } + + // PARENT PROCESS. Need to kill it. + if (process_id > 0) { + TRANSPORT_LOGE("Process id of child process %d", process_id); + // return success in exit status + exit(EXIT_SUCCESS); + } + + // unmask the file mode + umask(0); + + // set new session + sid = setsid(); + if (sid < 0) { + // Return failure + exit(EXIT_FAILURE); + } + + // Change the current working directory to root. + int ret = chdir("/"); + + if (ret < 0) { + throw errors::RuntimeException("Error changing working directory to root"); + } + + // Close stdin. Redirect stdout and stderr to file if possible + + if (close_fds) { + close(STDOUT_FILENO); + close(STDERR_FILENO); + } + + close(STDIN_FILENO); + + // Really start application +} + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/daemonizator.h b/libtransport/src/hicn/transport/utils/daemonizator.h new file mode 100755 index 000000000..a21ce8a7b --- /dev/null +++ b/libtransport/src/hicn/transport/utils/daemonizator.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include +namespace utils { + +class Daemonizator { + public: + static void daemonize(bool close_fds = true); +}; + +} // namespace utils \ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/deadline_timer.h b/libtransport/src/hicn/transport/utils/deadline_timer.h new file mode 100755 index 000000000..61f906141 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/deadline_timer.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include +#include +#include + +namespace std { +namespace chrono { +namespace detail { + +template +struct posix_duration_cast; + +// chrono -> timespec caster +template +struct posix_duration_cast, + struct timespec> { + static struct timespec cast(std::chrono::duration const &d) { + struct timespec tv; + + std::chrono::seconds const sec = + std::chrono::duration_cast(d); + + tv.tv_sec = sec.count(); + tv.tv_nsec = + std::chrono::duration_cast(d - sec).count(); + + return tv; + } +}; + +// timespec -> chrono caster +template +struct posix_duration_cast> { + static std::chrono::duration cast(struct timespec const &tv) { + return std::chrono::duration_cast>( + std::chrono::seconds(tv.tv_sec) + std::chrono::nanoseconds(tv.tv_nsec)); + } +}; + +} // namespace detail + +// chrono -> timespec +template +auto duration_cast(std::chrono::duration const &d) -> + typename std::enable_if::value, + struct timespec>::type { + return detail::posix_duration_cast, + timespec>::cast(d); +} + +// timespec -> chrono +template +Duration duration_cast(struct timespec const &tv) { + return detail::posix_duration_cast::cast(tv); +} + +} // namespace chrono +} // namespace std + +namespace utils { + +template +class DeadlineTimer { + public: + virtual ~DeadlineTimer() = default; + + template + void asyncWait(WaitHandler &&callback) { + static_cast(this)->asyncWaitImpl( + std::forward(callback)); + } + + void wait() { static_cast(this)->waitImpl(); } + + template + void expiresFromNow(std::chrono::duration &&duration) { + static_cast(this)->expiresFromNowImpl( + std::forward>(duration)); + } + + template , + std::chrono::steady_clock::time_point>::value, + TimePoint>::type> + void expiresAt(TimePoint &&time_point) { + static_cast(this)->expiresAtImpl( + std::forward(time_point)); + } + + void cancel() { static_cast(this)->cancelImpl(); } +}; + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/endianess.h b/libtransport/src/hicn/transport/utils/endianess.h new file mode 100755 index 000000000..a3ec21c90 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/endianess.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include + +namespace utils { + +namespace { + +template +struct uint_types_by_size; + +#define GENERATOR(sz, fn) \ + static TRANSPORT_ALWAYS_INLINE uint##sz##_t byteswap_gen(uint##sz##_t v) { \ + return fn(v); \ + } \ + template <> \ + struct uint_types_by_size { \ + using type = uint##sz##_t; \ + }; + +GENERATOR(8, uint8_t) +#ifdef _MSC_VER +GENERATOR(64, _byteswap_uint64) +GENERATOR(32, _byteswap_ulong) +GENERATOR(16, _byteswap_ushort) +#else +GENERATOR(64, __builtin_bswap64) +GENERATOR(32, __builtin_bswap32) +GENERATOR(16, __builtin_bswap16) +#endif + +template +struct EndianInt { + static_assert( + (std::is_integral::value && !std::is_same::value) || + std::is_floating_point::value, + "template type parameter must be non-bool integral or floating point"); + + static T swap(T x) { + // we implement this with memcpy because that is defined behavior in C++ + // we rely on compilers to optimize away the memcpy calls + constexpr auto s = sizeof(T); + using B = typename uint_types_by_size::type; + B b; + std::memcpy(&b, &x, s); + b = byteswap_gen(b); + std::memcpy(&x, &b, s); + return x; + } + static T big(T x) { + return portability::little_endian_arch ? EndianInt::swap(x) : x; + } + static T little(T x) { + return portability::big_endian_arch ? EndianInt::swap(x) : x; + } +}; + +} // namespace + +// big* convert between native and big-endian representations +// little* convert between native and little-endian representations +// swap* convert between big-endian and little-endian representations +// +// ntohs, htons == big16 +// ntohl, htonl == big32 +#define GENERATOR1(fn, t, sz) \ + static t fn##sz(t x) { return fn(x); } + +#define GENERATOR2(t, sz) \ + GENERATOR1(swap, t, sz) \ + GENERATOR1(big, t, sz) \ + GENERATOR1(little, t, sz) + +#define GENERATOR3(sz) \ + GENERATOR2(uint##sz##_t, sz) \ + GENERATOR2(int##sz##_t, sz) + +class Endian { + public: + enum class Order : uint8_t { LITTLE, BIG }; + + static constexpr Order order = + portability::little_endian_arch ? Order::LITTLE : Order::BIG; + + template + static T swap(T x) { + return EndianInt::swap(x); + } + + template + static T big(T x) { + return EndianInt::big(x); + } + + template + static T little(T x) { + return EndianInt::little(x); + } + +#if !defined(__ANDROID__) + GENERATOR3(64) + GENERATOR3(32) + GENERATOR3(16) + GENERATOR3(8) +#endif +}; + +template +static TRANSPORT_ALWAYS_INLINE T ntoh(T x) { + return Endian::order == Endian::Order::LITTLE ? Endian::little(x) : x; +} + +template +static TRANSPORT_ALWAYS_INLINE T hton(T x) { + return Endian::order == Endian::Order::LITTLE ? Endian::big(x) : x; +} + +} // namespace utils \ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/epoll_event_reactor.cc b/libtransport/src/hicn/transport/utils/epoll_event_reactor.cc new file mode 100755 index 000000000..81b471857 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/epoll_event_reactor.cc @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include + +namespace utils { + +EpollEventReactor::EpollEventReactor() + : epoll_fd_(epoll_create(20000)), run_event_loop_(true) {} + +EpollEventReactor::~EpollEventReactor() { close(epoll_fd_); } + +int EpollEventReactor::addFileDescriptor(int fd, uint32_t events) { + if (TRANSPORT_EXPECT_FALSE(fd < 0)) { + TRANSPORT_LOGE("invalid fd %d", fd); + return -1; + } + + struct epoll_event evt; + std::memset(&evt, 0, sizeof(evt)); + evt.events = events; + evt.data.fd = fd; + + if (TRANSPORT_EXPECT_FALSE(epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &evt) < + 0)) { + TRANSPORT_LOGE("epoll_ctl: %s fd %d", strerror(errno), fd); + return -1; + } + + return 0; +} + +int EpollEventReactor::addFileDescriptor(int fd, uint32_t events, + EventCallback &callback) { + auto it = event_callback_map_.find(fd); + event_callback_map_[fd] = callback; + if (it != event_callback_map_.end()) { + event_callback_map_[fd] = callback; + } else { + return addFileDescriptor(fd, events); + } + + return 0; +} + +int EpollEventReactor::addFileDescriptor(int fd, uint32_t events, + EventCallback &&callback) { + auto it = event_callback_map_.find(fd); + event_callback_map_[fd] = callback; + if (it != event_callback_map_.end()) { + event_callback_map_[fd] = callback; + } else { + return addFileDescriptor(fd, events); + } + + return 0; +} + +int EpollEventReactor::modFileDescriptor(int fd, uint32_t events) { + if (TRANSPORT_EXPECT_FALSE(fd < 0)) { + TRANSPORT_LOGE("invalid fd %d", fd); + return -1; + } + + struct epoll_event evt; + memset(&evt, 0, sizeof(evt)); + evt.events = events; + evt.data.fd = fd; + + if (TRANSPORT_EXPECT_FALSE(epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, fd, &evt) < + 0)) { + TRANSPORT_LOGE("epoll_ctl: %s fd %d", strerror(errno), fd); + return -1; + } + + return 0; +} + +std::size_t EpollEventReactor::mapSize() { return event_callback_map_.size(); } + +int EpollEventReactor::delFileDescriptor(int fd) { + if (TRANSPORT_EXPECT_FALSE(fd < 0)) { + TRANSPORT_LOGE("invalid fd %d", fd); + return -1; + } + + struct epoll_event evt; + memset(&evt, 0, sizeof(evt)); + + if (TRANSPORT_EXPECT_FALSE(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, &evt) < + 0)) { + TRANSPORT_LOGE("epoll_ctl: %s fd %d", strerror(errno), fd); + return -1; + } + + event_callback_map_.erase(fd); + + return 0; +} + +void EpollEventReactor::runEventLoop(int timeout) { + Event evt[128]; + int en = 0; + + // evt.events = EPOLLIN | EPOLLOUT; + sigset_t sigset; + sigemptyset(&sigset); + + while (run_event_loop_) { + memset(&evt, 0, sizeof(evt)); + + en = epoll_pwait(epoll_fd_, evt, 128, timeout, &sigset); + + if (TRANSPORT_EXPECT_FALSE(en < 0)) { + TRANSPORT_LOGE("epoll_pwait: %s", strerror(errno)); + return; + } + + for (int i = 0; i < en; i++) { + if (evt[i].data.fd > 0) { + auto it = event_callback_map_.find(evt[i].data.fd); + if (TRANSPORT_EXPECT_FALSE(it == event_callback_map_.end())) { + TRANSPORT_LOGE("unexpected event. fd %d", evt[i].data.fd); + } else { + event_callback_map_[evt[i].data.fd](evt[i]); + } + } else { + TRANSPORT_LOGE("unexpected event. fd %d", evt[i].data.fd); + } + } + } +} + +void EpollEventReactor::runOneEvent() { + Event evt; + int en = 0; + + // evt.events = EPOLLIN | EPOLLOUT; + sigset_t sigset; + sigemptyset(&sigset); + + memset(&evt, 0, sizeof(evt)); + + en = epoll_pwait(epoll_fd_, &evt, 1, -1, &sigset); + + if (TRANSPORT_EXPECT_FALSE(en < 0)) { + TRANSPORT_LOGE("epoll_pwait: %s", strerror(errno)); + return; + } + + if (TRANSPORT_EXPECT_TRUE(evt.data.fd > 0)) { + auto it = event_callback_map_.find(evt.data.fd); + if (TRANSPORT_EXPECT_FALSE(it == event_callback_map_.end())) { + TRANSPORT_LOGE("unexpected event. fd %d", evt.data.fd); + } else { + event_callback_map_[evt.data.fd](evt); + } + } else { + TRANSPORT_LOGE("unexpected event. fd %d", evt.data.fd); + } +} + +void EpollEventReactor::stop() { run_event_loop_ = false; } + +} // namespace utils \ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/epoll_event_reactor.h b/libtransport/src/hicn/transport/utils/epoll_event_reactor.h new file mode 100755 index 000000000..bb4db3ee7 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/epoll_event_reactor.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include + +#define FD_NUMBER 20000 + +namespace utils { + +typedef struct epoll_event Event; +typedef std::function EventCallback; +typedef std::unordered_map EventCallbackMap; + +class EpollEventReactor : public EventReactor { + public: + explicit EpollEventReactor(); + + ~EpollEventReactor(); + + int addFileDescriptor(int fd, uint32_t events, EventCallback &callback); + + int addFileDescriptor(int fd, uint32_t events, EventCallback &&callback); + + int delFileDescriptor(int fd); + + int modFileDescriptor(int fd, uint32_t events); + + void runEventLoop(int timeout = -1) override; + + void runOneEvent() override; + + void stop() override; + + std::size_t mapSize(); + + private: + int addFileDescriptor(int fd, uint32_t events); + + int epoll_fd_; + volatile bool run_event_loop_; + EventCallbackMap event_callback_map_; + std::mutex event_callback_map_mutex_; +}; + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/event_reactor.h b/libtransport/src/hicn/transport/utils/event_reactor.h new file mode 100755 index 000000000..4f8b58296 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/event_reactor.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +namespace utils { + +typedef std::function UserCallback; + +class EventReactor { + public: + virtual ~EventReactor() = default; + + virtual void runEventLoop(int timeout = -1) = 0; + + virtual void runOneEvent() = 0; + + virtual void stop() = 0; +}; + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/event_thread.h b/libtransport/src/hicn/transport/utils/event_thread.h new file mode 100755 index 000000000..3bf08c94b --- /dev/null +++ b/libtransport/src/hicn/transport/utils/event_thread.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include + +namespace utils { + +class EventThread { + private: + // No copies + EventThread(const EventThread&) = delete; // non construction-copyable + EventThread& operator=(const EventThread&) = delete; // non copyable + + public: + explicit EventThread(asio::io_service& io_service) + : internal_io_service_(nullptr), + io_service_(io_service), + work_(io_service_), + thread_(nullptr) { + run(); + } + + explicit EventThread() + : internal_io_service_(std::make_unique()), + io_service_(*internal_io_service_), + work_(io_service_), + thread_(nullptr) { + run(); + } + + ~EventThread() { stop(); } + + void run() { + if (stopped()) { + io_service_.reset(); + } + + thread_ = std::make_unique([this]() { io_service_.run(); }); + } + + std::thread::id getThreadId() const { + if (thread_) { + return thread_->get_id(); + } else { + throw errors::RuntimeException("Event thread is not running."); + } + } + + template + void add(Func&& f) { + // If the function f + // TODO USe post in mac os, asio->post in xenial + io_service_.post(std::forward(f)); + } + + template + void tryRunHandlerNow(Func&& f) { + io_service_.dispatch(std::forward(f)); + } + + void stop() { + TRANSPORT_LOGI("Stopping event thread!"); + + io_service_.stop(); + + if (thread_ && thread_->joinable()) { + thread_->join(); + } + + thread_.reset(); + } + + bool stopped() { return io_service_.stopped(); } + + asio::io_service& getIoService() { return io_service_; } + + private: + std::unique_ptr internal_io_service_; + asio::io_service& io_service_; + asio::io_service::work work_; + std::unique_ptr thread_; +}; + +} // namespace utils \ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/fd_deadline_timer.h b/libtransport/src/hicn/transport/utils/fd_deadline_timer.h new file mode 100755 index 000000000..3ed4590bc --- /dev/null +++ b/libtransport/src/hicn/transport/utils/fd_deadline_timer.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include +#include + +namespace utils { + +class FdDeadlineTimer : public DeadlineTimer { + public: + explicit FdDeadlineTimer(EpollEventReactor &reactor) + : reactor_(reactor), + timer_fd_(timerfd_create(CLOCK_MONOTONIC, 0)), + flags_(0) { + if (timer_fd_ == -1) { + throw errors::RuntimeException("Impossible to create the timer!"); + } + } + + ~FdDeadlineTimer() { close(timer_fd_); } + + template + void asyncWaitImpl(WaitHandler &&callback) { + // ASIO_WAIT_HANDLER_CHECK(WaitHandler, callback) type_check; + + if (timerfd_settime(timer_fd_, flags_, &new_value_, NULL) == -1) { + throw errors::RuntimeException("Impossible to set the timer!"); + } + + uint32_t events = EPOLLIN; + + reactor_.addFileDescriptor( + timer_fd_, events, + [callback{move(callback)}](const Event &event) -> int { + uint64_t s = 0; + std::error_code ec; + + if (read(event.data.fd, &s, sizeof(s)) == -1) { + TRANSPORT_LOGE("Read error!!"); + } + + if (!(event.events & EPOLLIN)) { + ec = std::make_error_code(std::errc::operation_canceled); + } + + callback(ec); + + return 0; + }); + } + + void waitImpl() { + if (timerfd_settime(timer_fd_, flags_, &new_value_, NULL) == -1) { + throw errors::RuntimeException("Impossible to set the timer!"); + } + + uint64_t ret; + + if (read(timer_fd_, &ret, sizeof(ret)) == -1) { + throw errors::RuntimeException( + "Error while waiting for the timer expiration."); + } + } + + template + void expiresFromNowImpl(std::chrono::duration &&duration) { + std::memset(&new_value_, 0, sizeof(new_value_)); + new_value_.it_value = std::chrono::duration_cast( + std::forward>(duration)); + } + + template , + std::chrono::steady_clock::time_point>::value, + TimePoint>> + void expiresAtImpl(TimePoint &&time_point) { + std::memset(&new_value_, 0, sizeof(new_value_)); + + new_value_.it_value = std::chrono::duration_cast( + time_point.time_since_epoch()); + flags_ |= TFD_TIMER_ABSTIME; + } + + void cancelImpl() { + std::memset(&new_value_, 0, sizeof(new_value_)); + + if (timerfd_settime(timer_fd_, 0, &new_value_, NULL) == -1) { + throw errors::RuntimeException("Impossible to cancel the timer!"); + } + + // reactor_.delFileDescriptor(timer_fd_); + } + + EventReactor &getEventReactor() { return reactor_; } + + private: + EpollEventReactor &reactor_; + int timer_fd_; + EventCallback callback_; + struct itimerspec new_value_; + int flags_; +}; + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/hash.h b/libtransport/src/hicn/transport/utils/hash.h new file mode 100755 index 000000000..6815ca4bf --- /dev/null +++ b/libtransport/src/hicn/transport/utils/hash.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright 2017 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace utils { + +namespace hash { + +/* + * Fowler / Noll / Vo (FNV) Hash + * http://www.isthe.com/chongo/tech/comp/fnv/ + */ + +const uint32_t FNV_32_HASH_START = 2166136261UL; +const uint64_t FNV_64_HASH_START = 14695981039346656037ULL; + +TRANSPORT_ALWAYS_INLINE uint32_t fnv32(const char *s, + uint32_t hash = FNV_32_HASH_START) { + for (; *s; ++s) { + hash += + (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24); + hash ^= *s; + } + return hash; +} + +TRANSPORT_ALWAYS_INLINE uint32_t fnv32_buf(const void *buf, size_t n, + uint32_t hash = FNV_32_HASH_START) { + // forcing signed char, since other platforms can use unsigned + const signed char *char_buf = reinterpret_cast(buf); + + for (size_t i = 0; i < n; ++i) { + hash += + (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24); + hash ^= char_buf[i]; + } + + return hash; +} + +TRANSPORT_ALWAYS_INLINE uint32_t fnv32(const std::string &str, + uint32_t hash = FNV_32_HASH_START) { + return fnv32_buf(str.data(), str.size(), hash); +} + +TRANSPORT_ALWAYS_INLINE uint64_t fnv64(const char *s, + uint64_t hash = FNV_64_HASH_START) { + for (; *s; ++s) { + hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) + + (hash << 8) + (hash << 40); + hash ^= *s; + } + return hash; +} + +TRANSPORT_ALWAYS_INLINE uint64_t fnv64_buf(const void *buf, size_t n, + uint64_t hash = FNV_64_HASH_START) { + // forcing signed char, since other platforms can use unsigned + const signed char *char_buf = reinterpret_cast(buf); + + for (size_t i = 0; i < n; ++i) { + hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) + + (hash << 8) + (hash << 40); + hash ^= char_buf[i]; + } + return hash; +} + +TRANSPORT_ALWAYS_INLINE uint64_t fnv64(const std::string &str, + uint64_t hash = FNV_64_HASH_START) { + return fnv64_buf(str.data(), str.size(), hash); +} + +} // namespace hash + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/identity.cc b/libtransport/src/hicn/transport/utils/identity.cc new file mode 100755 index 000000000..bdf7f29f9 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/identity.cc @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +extern "C" { +#include +#include +} + +namespace utils { + +Identity::Identity(const std::string &keystore_name, + const std::string &keystore_password, CryptoSuite suite, + unsigned int key_length, unsigned int validity_days, + const std::string &subject_name) { + parcSecurity_Init(); + + bool success = parcPkcs12KeyStore_CreateFile( + keystore_name.c_str(), keystore_password.c_str(), subject_name.c_str(), + parcCryptoSuite_GetSigningAlgorithm(static_cast(suite)), + key_length, validity_days); + + parcAssertTrue(success, + "parcPkcs12KeyStore_CreateFile('%s', '%s', '%s', %d, %d) failed.", + keystore_name.c_str(), keystore_password.c_str(), + subject_name.c_str(), static_cast(key_length), validity_days); + + PARCIdentityFile *identity_file = + parcIdentityFile_Create(keystore_name.c_str(), keystore_password.c_str()); + + identity_ = + parcIdentity_Create(identity_file, PARCIdentityFileAsPARCIdentity); + + PARCSigner *signer = parcIdentity_CreateSigner( + identity_, + parcCryptoSuite_GetCryptoHash(static_cast(suite))); + + signer_ = std::make_shared(signer); + + signature_length_ = parcSigner_GetSignatureSize(signer); + + parcSigner_Release(&signer); + parcIdentityFile_Release(&identity_file); +} + +Identity::Identity(const Identity &other) + : signer_(other.signer_), + hash_algorithm_(other.hash_algorithm_), + signature_length_(other.signature_length_) { + parcSecurity_Init(); + identity_ = parcIdentity_Acquire(other.identity_); +} + +Identity Identity::generateIdentity(const std::string &subject_name) { + std::string keystore_name = "keystore"; + std::string keystore_password = "password"; + std::size_t key_length = 1024; + unsigned int validity_days = 30; + CryptoSuite suite = CryptoSuite::RSA_SHA256; + + return utils::Identity(keystore_name, keystore_password, suite, key_length, + validity_days, subject_name); +} + +Identity::Identity(std::string &file_name, std::string &password, + transport::core::HashAlgorithm hash_algorithm) + : hash_algorithm_(hash_algorithm) { + parcSecurity_Init(); + + PARCIdentityFile *identity_file = + parcIdentityFile_Create(file_name.c_str(), password.c_str()); + + identity_ = + parcIdentity_Create(identity_file, PARCIdentityFileAsPARCIdentity); + + PARCSigner *signer = parcIdentity_CreateSigner( + identity_, static_cast(hash_algorithm)); + + signer_ = std::make_shared(signer); + + signature_length_ = parcSigner_GetSignatureSize(signer); + + parcSigner_Release(&signer); + parcIdentityFile_Release(&identity_file); +} + +// Identity::Identity(Identity &&other) { +// identity_ = parcIdentity_Acquire(other.identity_); +//} + +// Identity& Identity::operator=(const Identity& other) { +// signer_ = other.signer_; +// hash_algorithm_ = other.hash_algorithm_; +// signature_length_ = other.signature_length_; +// identity_ = parcIdentity_Acquire(other.identity_); + +// parcSecurity_Init(); +// } + +Identity::~Identity() { + parcIdentity_Release(&identity_); + parcSecurity_Fini(); +} + +std::string Identity::getFileName() { + return std::string(parcIdentity_GetFileName(identity_)); +} + +std::string Identity::getPassword() { + return std::string(parcIdentity_GetPassWord(identity_)); +} + +Signer &Identity::getSigner() { return *signer_; } + +unsigned int Identity::getSignatureLength() const { return signature_length_; } + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/identity.h b/libtransport/src/hicn/transport/utils/identity.h new file mode 100755 index 000000000..018842ee3 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/identity.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +extern "C" { +#include +#include +#include +}; + +#include + +namespace utils { + +class Identity { + public: + Identity(const std::string &keystore_name, + const std::string &keystore_password, CryptoSuite suite, + unsigned int signature_length, unsigned int validity_days, + const std::string &subject_name); + + // No copies + Identity(const Identity &other); + + Identity(std::string &file_name, std::string &password, + transport::core::HashAlgorithm hash_algorithm); + + ~Identity(); + + static Identity generateIdentity(const std::string &subject_name); + + std::string getFileName(); + + std::string getPassword(); + + Signer &getSigner(); + + unsigned int getSignatureLength() const; + + private: + PARCIdentity *identity_; + std::shared_ptr signer_; + transport::core::HashAlgorithm hash_algorithm_; + unsigned int signature_length_; +}; + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/key_id.h b/libtransport/src/hicn/transport/utils/key_id.h new file mode 100755 index 000000000..d67b73d7a --- /dev/null +++ b/libtransport/src/hicn/transport/utils/key_id.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace utils { + +using KeyId = std::pair; + +} diff --git a/libtransport/src/hicn/transport/utils/linux.h b/libtransport/src/hicn/transport/utils/linux.h new file mode 100755 index 000000000..5820528e1 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/linux.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef __linux__ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define LINK_LOCAL_PREFIX 0xfe80 + +namespace utils { + +static TRANSPORT_ALWAYS_INLINE int retrieveInterfaceAddress( + const std::string &interface_name, struct sockaddr_in6 *address) { + struct ifaddrs *ifap, *ifa; + char addr[INET6_ADDRSTRLEN]; + + getifaddrs(&ifap); + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family == AF_INET6 && + strcmp(ifa->ifa_name, interface_name.c_str()) == 0) { + struct sockaddr_in6 *tmp = (struct sockaddr_in6 *)ifa->ifa_addr; + uint16_t prefix = 0; + memcpy(&prefix, tmp->sin6_addr.s6_addr, sizeof(uint16_t)); + + if (htons(LINK_LOCAL_PREFIX) != prefix) { + *address = *(struct sockaddr_in6 *)ifa->ifa_addr; + getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), addr, + sizeof(addr), NULL, 0, NI_NUMERICHOST); + TRANSPORT_LOGI("Interface: %s\tAddress: %s", ifa->ifa_name, addr); + } + } + } + + freeifaddrs(ifap); + + return 0; +} + +} // namespace utils + +#endif // __linux__ \ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/literals.h b/libtransport/src/hicn/transport/utils/literals.h new file mode 100755 index 000000000..bd00e0a58 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/literals.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include + +TRANSPORT_ALWAYS_INLINE std::uint8_t operator"" _U8(unsigned long long value) { + return static_cast(value); +} + +TRANSPORT_ALWAYS_INLINE std::uint16_t operator"" _U16( + unsigned long long value) { + return static_cast(value); +} + +TRANSPORT_ALWAYS_INLINE std::uint32_t operator"" _U32( + unsigned long long value) { + return static_cast(value); +} + +TRANSPORT_ALWAYS_INLINE std::uint64_t operator"" _U64( + unsigned long long value) { + return static_cast(value); +} + +TRANSPORT_ALWAYS_INLINE std::int8_t operator"" _I8(unsigned long long value) { + return static_cast(value); +} + +TRANSPORT_ALWAYS_INLINE std::int16_t operator"" _I16(unsigned long long value) { + return static_cast(value); +} + +TRANSPORT_ALWAYS_INLINE std::int32_t operator"" _I32(unsigned long long value) { + return static_cast(value); +} + +TRANSPORT_ALWAYS_INLINE std::int64_t operator"" _I64(unsigned long long value) { + return static_cast(value); +} \ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/log.cc b/libtransport/src/hicn/transport/utils/log.cc new file mode 100755 index 000000000..064625ec0 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/log.cc @@ -0,0 +1,1405 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 wonder-mice + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* When defined, Android log (android/log.h) will be used by default instead of + * stderr (ignored on non-Android platforms). Date, time, pid and tid (context) + * will be provided by Android log. Android log features will be used to output + * log level and tag. + */ +#ifdef TRANSPORT_LOG_USE_ANDROID_LOG +#undef TRANSPORT_LOG_USE_ANDROID_LOG +#if defined(__ANDROID__) +#define TRANSPORT_LOG_USE_ANDROID_LOG 1 +#else +#define TRANSPORT_LOG_USE_ANDROID_LOG 0 +#endif +#else +#define TRANSPORT_LOG_USE_ANDROID_LOG 0 +#endif +/* When defined, NSLog (uses Apple System Log) will be used instead of stderr + * (ignored on non-Apple platforms). Date, time, pid and tid (context) will be + * provided by NSLog. Curiously, doesn't use NSLog() directly, but piggybacks on + * non-public CFLog() function. Both use Apple System Log internally, but it's + * easier to call CFLog() from C than NSLog(). Current implementation doesn't + * support "%@" format specifier. + */ +#ifdef TRANSPORT_LOG_USE_NSLOG +#undef TRANSPORT_LOG_USE_NSLOG +#if defined(__APPLE__) && defined(__MACH__) +#define TRANSPORT_LOG_USE_NSLOG 1 +#else +#define TRANSPORT_LOG_USE_NSLOG 0 +#endif +#else +#define TRANSPORT_LOG_USE_NSLOG 0 +#endif +/* When defined, OutputDebugString() will be used instead of stderr (ignored on + * non-Windows platforms). Uses OutputDebugStringA() variant and feeds it with + * UTF-8 data. + */ +#ifdef TRANSPORT_LOG_USE_DEBUGSTRING +#undef TRANSPORT_LOG_USE_DEBUGSTRING +#if defined(_WIN32) || defined(_WIN64) +#define TRANSPORT_LOG_USE_DEBUGSTRING 1 +#else +#define TRANSPORT_LOG_USE_DEBUGSTRING 0 +#endif +#else +#define TRANSPORT_LOG_USE_DEBUGSTRING 0 +#endif +/* When defined, TRANSPORT_LOG library will not contain definition of tag prefix + * variable. In that case it must be defined elsewhere using + * TRANSPORT_LOG_DEFINE_TAG_PREFIX macro, for example: + * + * TRANSPORT_LOG_DEFINE_TAG_PREFIX = "ProcessName"; + * + * This allows to specify custom value for static initialization and avoid + * overhead of setting this value in runtime. + */ +#ifdef TRANSPORT_LOG_EXTERN_TAG_PREFIX +#undef TRANSPORT_LOG_EXTERN_TAG_PREFIX +#define TRANSPORT_LOG_EXTERN_TAG_PREFIX 1 +#else +#define TRANSPORT_LOG_EXTERN_TAG_PREFIX 0 +#endif +/* When defined, TRANSPORT_LOG library will not contain definition of global + * format variable. In that case it must be defined elsewhere using + * TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT macro, for example: + * + * TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT = {MEM_WIDTH}; + * + * This allows to specify custom value for static initialization and avoid + * overhead of setting this value in runtime. + */ +#ifdef TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT +#undef TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT +#define TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT 1 +#else +#define TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT 0 +#endif +/* When defined, transport_log library will not contain definition of global + * output variable. In that case it must be defined elsewhere using + * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT macro, for example: + * + * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {TRANSPORT_LOG_PUT_STD, + * custom_output_callback}; + * + * This allows to specify custom value for static initialization and avoid + * overhead of setting this value in runtime. + */ +#ifdef TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT +#undef TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT +#define TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT 1 +#else +#define TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT 0 +#endif +/* When defined, transport_log library will not contain definition of global + * output level variable. In that case it must be defined elsewhere using + * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL macro, for example: + * + * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = TRANSPORT_LOG_WARN; + * + * This allows to specify custom value for static initialization and avoid + * overhead of setting this value in runtime. + */ +#ifdef TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL +#undef TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL +#define TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL 1 +#else +#define TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL 0 +#endif +/* When defined, implementation will prefer smaller code size over speed. + * Very rough estimate is that code will be up to 2x smaller and up to 2x + * slower. Disabled by default. + */ +#ifdef TRANSPORT_LOG_OPTIMIZE_SIZE +#undef TRANSPORT_LOG_OPTIMIZE_SIZE +#define TRANSPORT_LOG_OPTIMIZE_SIZE 1 +#else +#define TRANSPORT_LOG_OPTIMIZE_SIZE 0 +#endif +/* Size of the log line buffer. The buffer is allocated on stack. It limits + * maximum length of a log line. + */ +#ifndef TRANSPORT_LOG_BUF_SZ +#define TRANSPORT_LOG_BUF_SZ 512 +#endif +/* Default number of bytes in one line of memory output. For large values + * TRANSPORT_LOG_BUF_SZ also must be increased. + */ +#ifndef TRANSPORT_LOG_MEM_WIDTH +#define TRANSPORT_LOG_MEM_WIDTH 32 +#endif +/* String to put in the end of each log line (can be empty). Its value used by + * stderr output callback. Its size used as a default value for + * TRANSPORT_LOG_EOL_SZ. + */ +#ifndef TRANSPORT_LOG_EOL +#define TRANSPORT_LOG_EOL "\n" +#endif +/* Default delimiter that separates parts of log message. Can NOT contain '%' + * or '\0'. + * + * Log message format specifications can override (or ignore) this value. For + * more details see TRANSPORT_LOG_MESSAGE_CTX_FORMAT, + * TRANSPORT_LOG_MESSAGE_SRC_FORMAT and TRANSPORT_LOG_MESSAGE_TAG_FORMAT. + */ +#ifndef TRANSPORT_LOG_DEF_DELIMITER +#define TRANSPORT_LOG_DEF_DELIMITER " " +#endif +/* Specifies log message context format. Log message context includes date, + * time, process id, thread id and message's log level. Custom information can + * be added as well. Supported fields: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, + * MILLISECOND, PID, TID, LEVEL, S(str), F_INIT(statements), + * F_UINT(width, value). + * + * Must be defined as a tuple, for example: + * + * #define TRANSPORT_LOG_MESSAGE_CTX_FORMAT (YEAR, S("."), MONTH, S("."), DAY, + * S(" > ")) + * + * In that case, resulting log message will be: + * + * 2016.12.22 > TAG function@filename.c:line Message text + * + * Note, that tag, source location and message text are not impacted by + * this setting. See TRANSPORT_LOG_MESSAGE_TAG_FORMAT and + * TRANSPORT_LOG_MESSAGE_SRC_FORMAT. + * + * If message context must be visually separated from the rest of the message, + * it must be reflected in context format (notice trailing S(" > ") in the + * example above). + * + * S(str) adds constant string str. String can NOT contain '%' or '\0'. + * + * F_INIT(statements) adds initialization statement(s) that will be evaluated + * once for each log message. All statements are evaluated in specified order. + * Several F_INIT() fields can be used in every log message format + * specification. Fields, like F_UINT(width, value), are allowed to use results + * of initialization statements. If statement introduces variables (or other + * names, like structures) they must be prefixed with "f_". Statements must be + * enclosed into additional "()". Example: + * + * #define TRANSPORT_LOG_MESSAGE_CTX_FORMAT \ + * (F_INIT(( struct rusage f_ru; getrusage(RUSAGE_SELF, &f_ru); )), \ + * YEAR, S("."), MONTH, S("."), DAY, S(" "), \ + * F_UINT(5, f_ru.ru_nsignals), \ + * S(" ")) + * + * F_UINT(width, value) adds unsigned integer value extended with up to width + * spaces (for alignment purposes). Value can be any expression that evaluates + * to unsigned integer. If expression contains non-standard functions, they + * must be declared with F_INIT(). Example: + * + * #define TRANSPORT_LOG_MESSAGE_CTX_FORMAT \ + * (YEAR, S("."), MONTH, S("."), DAY, S(" "), \ + * F_INIT(( unsigned tickcount(); )), \ + * F_UINT(5, tickcount()), \ + * S(" ")) + * + * Other log message format specifications follow same rules, but have a + * different set of supported fields. + */ +#ifndef TRANSPORT_LOG_MESSAGE_CTX_FORMAT +#define TRANSPORT_LOG_MESSAGE_CTX_FORMAT \ + (MONTH, S("-"), DAY, S(TRANSPORT_LOG_DEF_DELIMITER), HOUR, S(":"), MINUTE, \ + S(":"), SECOND, S("."), MILLISECOND, S(TRANSPORT_LOG_DEF_DELIMITER), PID, \ + S(TRANSPORT_LOG_DEF_DELIMITER), TID, S(TRANSPORT_LOG_DEF_DELIMITER), LEVEL, \ + S(TRANSPORT_LOG_DEF_DELIMITER)) +#endif +/* Example: + */ +/* Specifies log message tag format. It includes tag prefix and tag. Custom + * information can be added as well. Supported fields: + * TAG(prefix_delimiter, tag_delimiter), S(str), F_INIT(statements), + * F_UINT(width, value). + * + * TAG(prefix_delimiter, tag_delimiter) adds following string to log message: + * + * PREFIXTAG + * + * Prefix delimiter will be used only when prefix is not empty. Tag delimiter + * will be used only when prefixed tag is not empty. Example: + * + * #define TRANSPORT_LOG_TAG_FORMAT (S("["), TAG(".", ""), S("] ")) + * + * See TRANSPORT_LOG_MESSAGE_CTX_FORMAT for details. + */ +#ifndef TRANSPORT_LOG_MESSAGE_TAG_FORMAT +#define TRANSPORT_LOG_MESSAGE_TAG_FORMAT (TAG(".", TRANSPORT_LOG_DEF_DELIMITER)) +#endif +/* Specifies log message source location format. It includes function name, + * file name and file line. Custom information can be added as well. Supported + * fields: FUNCTION, FILENAME, FILELINE, S(str), F_INIT(statements), + * F_UINT(width, value). + * + * See TRANSPORT_LOG_MESSAGE_CTX_FORMAT for details. + */ +#ifndef TRANSPORT_LOG_MESSAGE_SRC_FORMAT +#define TRANSPORT_LOG_MESSAGE_SRC_FORMAT \ + (FUNCTION, S("@"), FILENAME, S(":"), FILELINE, S(TRANSPORT_LOG_DEF_DELIMITER)) +#endif +/* Fields that can be used in log message format specifications (see above). + * Mentioning them here explicitly, so we know that nobody else defined them + * before us. See TRANSPORT_LOG_MESSAGE_CTX_FORMAT for details. + */ +#define YEAR YEAR +#define MONTH MONTH +#define DAY DAY +#define MINUTE MINUTE +#define SECOND SECOND +#define MILLISECOND MILLISECOND +#define PID PID +#define TID TID +#define LEVEL LEVEL +#define TAG(prefix_delim, tag_delim) TAG(prefix_delim, tag_delim) +#define FUNCTION FUNCTION +#define FILENAME FILENAME +#define FILELINE FILELINE +#define S(str) S(str) +#define F_INIT(statements) F_INIT(statements) +#define F_UINT(width, value) F_UINT(width, value) +/* Number of bytes to reserve for EOL in the log line buffer (must be >0). + * Must be larger than or equal to length of TRANSPORT_LOG_EOL with terminating + * null. + */ +#ifndef TRANSPORT_LOG_EOL_SZ +#define TRANSPORT_LOG_EOL_SZ sizeof(TRANSPORT_LOG_EOL) +#endif +/* Compile instrumented version of the library to facilitate unit testing. + */ +#ifndef TRANSPORT_LOG_INSTRUMENTED +#define TRANSPORT_LOG_INSTRUMENTED 0 +#endif + +#if defined(__linux__) +#if !defined(__ANDROID__) && !defined(_GNU_SOURCE) +#define _GNU_SOURCE +#endif +#endif +#if defined(__MINGW32__) +#ifdef __STRICT_ANSI__ +#undef __STRICT_ANSI__ +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include +#include +#if defined(__linux__) +#include +#else +#include +#endif +#endif + +#if defined(__linux__) +#include +#include +#if !defined(__ANDROID__) +#include +#endif +#endif +#if defined(__MACH__) +#include +#endif + +#define INLINE _TRANSPORT_LOG_INLINE +#define VAR_UNUSED(var) (void)var +#define RETVAL_UNUSED(expr) \ + do { \ + while (expr) break; \ + } while (0) +#define STATIC_ASSERT(name, cond) typedef char assert_##name[(cond) ? 1 : -1] +#define ASSERT_UNREACHABLE(why) assert(!sizeof(why)) +#ifndef _countof +#define _countof(xs) (sizeof(xs) / sizeof((xs)[0])) +#endif + +#if TRANSPORT_LOG_INSTRUMENTED +#define INSTRUMENTED_CONST +#else +#define INSTRUMENTED_CONST const +#endif + +#define _PP_PASTE_2(a, b) a##b +#define _PP_CONCAT_2(a, b) _PP_PASTE_2(a, b) + +#define _PP_PASTE_3(a, b, c) a##b##c +#define _PP_CONCAT_3(a, b, c) _PP_PASTE_3(a, b, c) + +/* Microsoft C preprocessor is a piece of shit. This moron treats __VA_ARGS__ + * as a single token and requires additional expansion to realize that it's + * actually a list. If not for it, there would be no need in this extra + * expansion. + */ +#define _PP_ID(x) x +#define _PP_NARGS_N(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, \ + _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, \ + _24, ...) \ + _24 +#define _PP_NARGS(...) \ + _PP_ID(_PP_NARGS_N(__VA_ARGS__, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, \ + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)) + +/* There is a more efficient way to implement this, but it requires + * working C preprocessor. Unfortunately, Microsoft Visual Studio doesn't + * have one. + */ +#define _PP_HEAD__(x, ...) x +#define _PP_HEAD_(...) _PP_ID(_PP_HEAD__(__VA_ARGS__, ~)) +#define _PP_HEAD(xs) _PP_HEAD_ xs +#define _PP_TAIL_(x, ...) (__VA_ARGS__) +#define _PP_TAIL(xs) _PP_TAIL_ xs +#define _PP_UNTUPLE_(...) __VA_ARGS__ +#define _PP_UNTUPLE(xs) _PP_UNTUPLE_ xs + +/* Apply function macro to each element in tuple. Output is not + * enforced to be a tuple. + */ +#define _PP_MAP_1(f, xs) f(_PP_HEAD(xs)) +#define _PP_MAP_2(f, xs) f(_PP_HEAD(xs)) _PP_MAP_1(f, _PP_TAIL(xs)) +#define _PP_MAP_3(f, xs) f(_PP_HEAD(xs)) _PP_MAP_2(f, _PP_TAIL(xs)) +#define _PP_MAP_4(f, xs) f(_PP_HEAD(xs)) _PP_MAP_3(f, _PP_TAIL(xs)) +#define _PP_MAP_5(f, xs) f(_PP_HEAD(xs)) _PP_MAP_4(f, _PP_TAIL(xs)) +#define _PP_MAP_6(f, xs) f(_PP_HEAD(xs)) _PP_MAP_5(f, _PP_TAIL(xs)) +#define _PP_MAP_7(f, xs) f(_PP_HEAD(xs)) _PP_MAP_6(f, _PP_TAIL(xs)) +#define _PP_MAP_8(f, xs) f(_PP_HEAD(xs)) _PP_MAP_7(f, _PP_TAIL(xs)) +#define _PP_MAP_9(f, xs) f(_PP_HEAD(xs)) _PP_MAP_8(f, _PP_TAIL(xs)) +#define _PP_MAP_10(f, xs) f(_PP_HEAD(xs)) _PP_MAP_9(f, _PP_TAIL(xs)) +#define _PP_MAP_11(f, xs) f(_PP_HEAD(xs)) _PP_MAP_10(f, _PP_TAIL(xs)) +#define _PP_MAP_12(f, xs) f(_PP_HEAD(xs)) _PP_MAP_11(f, _PP_TAIL(xs)) +#define _PP_MAP_13(f, xs) f(_PP_HEAD(xs)) _PP_MAP_12(f, _PP_TAIL(xs)) +#define _PP_MAP_14(f, xs) f(_PP_HEAD(xs)) _PP_MAP_13(f, _PP_TAIL(xs)) +#define _PP_MAP_15(f, xs) f(_PP_HEAD(xs)) _PP_MAP_14(f, _PP_TAIL(xs)) +#define _PP_MAP_16(f, xs) f(_PP_HEAD(xs)) _PP_MAP_15(f, _PP_TAIL(xs)) +#define _PP_MAP_17(f, xs) f(_PP_HEAD(xs)) _PP_MAP_16(f, _PP_TAIL(xs)) +#define _PP_MAP_18(f, xs) f(_PP_HEAD(xs)) _PP_MAP_17(f, _PP_TAIL(xs)) +#define _PP_MAP_19(f, xs) f(_PP_HEAD(xs)) _PP_MAP_18(f, _PP_TAIL(xs)) +#define _PP_MAP_20(f, xs) f(_PP_HEAD(xs)) _PP_MAP_19(f, _PP_TAIL(xs)) +#define _PP_MAP_21(f, xs) f(_PP_HEAD(xs)) _PP_MAP_20(f, _PP_TAIL(xs)) +#define _PP_MAP_22(f, xs) f(_PP_HEAD(xs)) _PP_MAP_21(f, _PP_TAIL(xs)) +#define _PP_MAP_23(f, xs) f(_PP_HEAD(xs)) _PP_MAP_22(f, _PP_TAIL(xs)) +#define _PP_MAP_24(f, xs) f(_PP_HEAD(xs)) _PP_MAP_23(f, _PP_TAIL(xs)) +#define _PP_MAP(f, xs) _PP_CONCAT_2(_PP_MAP_, _PP_NARGS xs)(f, xs) + +/* Apply function macro to each element in tuple in reverse order. + * Output is not enforced to be a tuple. + */ +#define _PP_RMAP_1(f, xs) f(_PP_HEAD(xs)) +#define _PP_RMAP_2(f, xs) _PP_RMAP_1(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_3(f, xs) _PP_RMAP_2(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_4(f, xs) _PP_RMAP_3(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_5(f, xs) _PP_RMAP_4(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_6(f, xs) _PP_RMAP_5(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_7(f, xs) _PP_RMAP_6(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_8(f, xs) _PP_RMAP_7(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_9(f, xs) _PP_RMAP_8(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_10(f, xs) _PP_RMAP_9(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_11(f, xs) _PP_RMAP_10(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_12(f, xs) _PP_RMAP_11(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_13(f, xs) _PP_RMAP_12(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_14(f, xs) _PP_RMAP_13(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_15(f, xs) _PP_RMAP_14(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_16(f, xs) _PP_RMAP_15(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_17(f, xs) _PP_RMAP_16(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_18(f, xs) _PP_RMAP_17(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_19(f, xs) _PP_RMAP_18(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_20(f, xs) _PP_RMAP_19(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_21(f, xs) _PP_RMAP_20(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_22(f, xs) _PP_RMAP_21(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_23(f, xs) _PP_RMAP_22(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_24(f, xs) _PP_RMAP_23(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP(f, xs) _PP_CONCAT_2(_PP_RMAP_, _PP_NARGS xs)(f, xs) + +/* Used to implement _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS() macro. All + * possible fields must be mentioned here. Not counting F_INIT() here because + * it's somewhat special and is handled spearatly (at least for now). + */ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__ (0 << 0) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__YEAR (1 << 1) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__MONTH (1 << 2) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__DAY (1 << 3) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__HOUR (1 << 4) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__MINUTE (1 << 5) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__SECOND (1 << 6) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__MILLISECOND (1 << 7) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__PID (1 << 8) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__TID (1 << 9) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__LEVEL (1 << 10) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__TAG(ps, ts) (1 << 11) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__FUNCTION (1 << 12) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__FILENAME (1 << 13) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__FILELINE (1 << 14) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__S(s) (1 << 15) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__F_INIT(expr) (0 << 16) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__F_UINT(w, v) (1 << 17) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK(field) \ + _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_MASK_, _, field) + +/* Logical "or" of masks of fields used in specified format specification. + */ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(format) \ + (0 _PP_MAP(| _TRANSPORT_LOG_MESSAGE_FORMAT_MASK, format)) + +/* Expands to expressions that evaluates to true if field is used in + * specified format specification. Example: + * + * #if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(F_UINT, + * TRANSPORT_LOG_MESSAGE_CTX_FORMAT) + * ... + * #endif + */ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(field, format) \ + (_TRANSPORT_LOG_MESSAGE_FORMAT_MASK(field) & \ + _TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(format)) + +/* Same, but checks all supported format specifications. + */ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_FIELD_USED(field) \ + (_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(field, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ + _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(field, \ + TRANSPORT_LOG_MESSAGE_TAG_FORMAT) || \ + _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(field, \ + TRANSPORT_LOG_MESSAGE_SRC_FORMAT)) + +#define _TRANSPORT_LOG_MESSAGE_FORMAT_DATETIME_USED \ + (_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(YEAR, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ + _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(MONTH, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ + _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(DAY, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ + _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(HOUR, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ + _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(MINUTE, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ + _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(SECOND, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ + _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(MILLISECOND, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT)) + +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) +#pragma warning(disable : 4204) /* nonstandard extension used: non-constant \ + aggregate initializer */ +#define memccpy _memccpy +#endif + +#if (defined(_MSC_VER) && !defined(__INTEL_COMPILER)) || defined(__MINGW64__) +#define vsnprintf(s, sz, fmt, va) fake_vsnprintf(s, sz, fmt, va) +static int fake_vsnprintf(char *s, size_t sz, const char *fmt, va_list ap) { + const int n = vsnprintf_s(s, sz, _TRUNCATE, fmt, ap); + return 0 < n ? n : (int)sz + 1; /* no need in _vscprintf() for now */ +} +#if TRANSPORT_LOG_OPTIMIZE_SIZE +#define snprintf(s, sz, ...) fake_snprintf(s, sz, __VA_ARGS__) +static int fake_snprintf(char *s, size_t sz, const char *fmt, ...) { + va_list va; + va_start(va, fmt); + const int n = fake_vsnprintf(s, sz, fmt, va); + va_end(va); + return n; +} +#endif +#endif + +typedef void (*time_cb)(struct tm *const tm, unsigned *const usec); +typedef void (*pid_cb)(int *const pid, int *const tid); +typedef void (*buffer_cb)(transport_log_message *msg, char *buf); + +typedef struct src_location { + const char *const func; + const char *const file; + const unsigned line; +} src_location; + +typedef struct mem_block { + const void *const d; + const unsigned d_sz; +} mem_block; + +static void time_callback(struct tm *const tm, unsigned *const usec); +static void pid_callback(int *const pid, int *const tid); +static void buffer_callback(transport_log_message *msg, char *buf); + +STATIC_ASSERT(eol_fits_eol_sz, + sizeof(TRANSPORT_LOG_EOL) <= TRANSPORT_LOG_EOL_SZ); +STATIC_ASSERT(eol_sz_greater_than_zero, 0 < TRANSPORT_LOG_EOL_SZ); +STATIC_ASSERT(eol_sz_less_than_buf_sz, + TRANSPORT_LOG_EOL_SZ < TRANSPORT_LOG_BUF_SZ); +#if !defined(_WIN32) && !defined(_WIN64) +STATIC_ASSERT(buf_sz_less_than_pipe_buf, TRANSPORT_LOG_BUF_SZ <= PIPE_BUF); +#endif +static const char c_hex[] = "0123456789abcdef"; + +static INSTRUMENTED_CONST unsigned g_buf_sz = + TRANSPORT_LOG_BUF_SZ - TRANSPORT_LOG_EOL_SZ; +static INSTRUMENTED_CONST time_cb g_time_cb = time_callback; +static INSTRUMENTED_CONST pid_cb g_pid_cb = pid_callback; +static INSTRUMENTED_CONST buffer_cb g_buffer_cb = buffer_callback; + +#if TRANSPORT_LOG_USE_ANDROID_LOG +#include + +static INLINE int android_lvl(const int lvl) { + switch (lvl) { + case TRANSPORT_LOG_VERBOSE: + return ANDROID_LOG_VERBOSE; + case TRANSPORT_LOG_DEBUG: + return ANDROID_LOG_DEBUG; + case TRANSPORT_LOG_INFO: + return ANDROID_LOG_INFO; + case TRANSPORT_LOG_WARN: + return ANDROID_LOG_WARN; + case TRANSPORT_LOG_ERROR: + return ANDROID_LOG_ERROR; + case TRANSPORT_LOG_FATAL: + return ANDROID_LOG_FATAL; + default: + ASSERT_UNREACHABLE("Bad log level"); + return ANDROID_LOG_UNKNOWN; + } +} + +static void out_android_callback(const transport_log_message *const msg, + void *arg) { + VAR_UNUSED(arg); + *msg->p = 0; + const char *tag = msg->p; + if (msg->tag_e != msg->tag_b) { + tag = msg->tag_b; + *msg->tag_e = 0; + } + __android_log_print(android_lvl(msg->lvl), tag, "%s", msg->msg_b); +} + +enum { OUT_ANDROID_MASK = TRANSPORT_LOG_PUT_STD & ~TRANSPORT_LOG_PUT_CTX }; +#define OUT_ANDROID OUT_ANDROID_MASK, 0, out_android_callback +#endif + +#if TRANSPORT_LOG_USE_NSLOG +#include +CF_EXPORT void CFLog(int32_t level, CFStringRef format, ...); + +static INLINE int apple_lvl(const int lvl) { + switch (lvl) { + case TRANSPORT_LOG_VERBOSE: + return 7; /* ASL_LEVEL_DEBUG / kCFLogLevelDebug */ + ; + case TRANSPORT_LOG_DEBUG: + return 7; /* ASL_LEVEL_DEBUG / kCFLogLevelDebug */ + ; + case TRANSPORT_LOG_INFO: + return 6; /* ASL_LEVEL_INFO / kCFLogLevelInfo */ + ; + case TRANSPORT_LOG_WARN: + return 4; /* ASL_LEVEL_WARNING / kCFLogLevelWarning */ + ; + case TRANSPORT_LOG_ERROR: + return 3; /* ASL_LEVEL_ERR / kCFLogLevelError */ + ; + case TRANSPORT_LOG_FATAL: + return 0; /* ASL_LEVEL_EMERG / kCFLogLevelEmergency */ + ; + default: + ASSERT_UNREACHABLE("Bad log level"); + return 0; /* ASL_LEVEL_EMERG / kCFLogLevelEmergency */ + ; + } +} + +static void out_nslog_callback(const transport_log_message *const msg, + void *arg) { + VAR_UNUSED(arg); + *msg->p = 0; + CFLog(apple_lvl(msg->lvl), CFSTR("%s"), msg->tag_b); +} + +enum { OUT_NSLOG_MASK = TRANSPORT_LOG_PUT_STD & ~TRANSPORT_LOG_PUT_CTX }; +#define OUT_NSLOG OUT_NSLOG_MASK, 0, out_nslog_callback +#endif + +#if TRANSPORT_LOG_USE_DEBUGSTRING +#include + +static void out_debugstring_callback(const transport_log_message *const msg, + void *arg) { + VAR_UNUSED(arg); + msg->p[0] = '\n'; + msg->p[1] = '\0'; + OutputDebugStringA(msg->buf); +} + +enum { OUT_DEBUGSTRING_MASK = TRANSPORT_LOG_PUT_STD }; +#define OUT_DEBUGSTRING OUT_DEBUGSTRING_MASK, 0, out_debugstring_callback +#endif + +void transport_log_out_stderr_callback(const transport_log_message *const msg, + void *arg) { + VAR_UNUSED(arg); + const size_t eol_len = sizeof(TRANSPORT_LOG_EOL) - 1; + memcpy(msg->p, TRANSPORT_LOG_EOL, eol_len); +#if defined(_WIN32) || defined(_WIN64) + /* WriteFile() is atomic for local files opened with FILE_APPEND_DATA and + without FILE_WRITE_DATA */ + DWORD written; + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg->buf, + (DWORD)(msg->p - msg->buf + eol_len), &written, 0); +#else + /* write() is atomic for buffers less than or equal to PIPE_BUF. */ + RETVAL_UNUSED( + write(STDERR_FILENO, msg->buf, (size_t)(msg->p - msg->buf) + eol_len)); +#endif +} + +static const transport_log_output out_stderr = {TRANSPORT_LOG_OUT_STDERR}; + +#if !TRANSPORT_LOG_EXTERN_TAG_PREFIX +TRANSPORT_LOG_DEFINE_TAG_PREFIX = 0; +#endif + +#if !TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT +TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT = {TRANSPORT_LOG_MEM_WIDTH}; +#endif + +#if !TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT +#if TRANSPORT_LOG_USE_ANDROID_LOG +TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_ANDROID}; +#elif TRANSPORT_LOG_USE_NSLOG +TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_NSLOG}; +#elif TRANSPORT_LOG_USE_DEBUGSTRING +TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_DEBUGSTRING}; +#else +TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {TRANSPORT_LOG_OUT_STDERR}; +#endif +#endif + +#if !TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL +TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = 0; +#endif + +const transport_log_spec _transport_log_stderr_spec = { + TRANSPORT_LOG_GLOBAL_FORMAT, + &out_stderr, +}; + +static const transport_log_spec global_spec = { + TRANSPORT_LOG_GLOBAL_FORMAT, + TRANSPORT_LOG_GLOBAL_OUTPUT, +}; + +#if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(LEVEL, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) +static char lvl_char(const int lvl) { + switch (lvl) { + case TRANSPORT_LOG_VERBOSE: + return 'V'; + case TRANSPORT_LOG_DEBUG: + return 'D'; + case TRANSPORT_LOG_INFO: + return 'I'; + case TRANSPORT_LOG_WARN: + return 'W'; + case TRANSPORT_LOG_ERROR: + return 'E'; + case TRANSPORT_LOG_FATAL: + return 'F'; + default: + ASSERT_UNREACHABLE("Bad log level"); + return '?'; + } +} +#endif + +#define GCCVER_LESS(MAJOR, MINOR, PATCH) \ + (__GNUC__ < MAJOR || (__GNUC__ == MAJOR && (__GNUC_MINOR__ < MINOR || \ + (__GNUC_MINOR__ == MINOR && \ + __GNUC_PATCHLEVEL__ < PATCH)))) + +#if !defined(__clang__) && defined(__GNUC__) && GCCVER_LESS(4, 7, 0) +#define __atomic_load_n(vp, model) __sync_fetch_and_add(vp, 0) +#define __atomic_fetch_add(vp, n, model) __sync_fetch_and_add(vp, n) +#define __atomic_sub_fetch(vp, n, model) __sync_sub_and_fetch(vp, n) +#define __atomic_or_fetch(vp, n, model) __sync_or_and_fetch(vp, n) +#define __atomic_and_fetch(vp, n, model) __sync_and_and_fetch(vp, n) +/* Note: will not store old value of *vp in *ep (non-standard behaviour) */ +#define __atomic_compare_exchange_n(vp, ep, d, weak, smodel, fmodel) \ + __sync_bool_compare_and_swap(vp, *(ep), d) +#endif + +#if !TRANSPORT_LOG_OPTIMIZE_SIZE && !defined(_WIN32) && !defined(_WIN64) +#define TCACHE +#define TCACHE_STALE (0x40000000) +#define TCACHE_FLUID (0x40000000 | 0x80000000) +static unsigned g_tcache_mode = TCACHE_STALE; +static struct timeval g_tcache_tv = {0, 0}; +static struct tm g_tcache_tm = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +static INLINE int tcache_get(const struct timeval *const tv, + struct tm *const tm) { + unsigned mode; + mode = __atomic_load_n(&g_tcache_mode, __ATOMIC_RELAXED); + if (0 == (mode & TCACHE_FLUID)) { + mode = __atomic_fetch_add(&g_tcache_mode, 1, __ATOMIC_ACQUIRE); + if (0 == (mode & TCACHE_FLUID)) { + if (g_tcache_tv.tv_sec == tv->tv_sec) { + *tm = g_tcache_tm; + __atomic_sub_fetch(&g_tcache_mode, 1, __ATOMIC_RELEASE); + return !0; + } + __atomic_or_fetch(&g_tcache_mode, TCACHE_STALE, __ATOMIC_RELAXED); + } + __atomic_sub_fetch(&g_tcache_mode, 1, __ATOMIC_RELEASE); + } + return 0; +} + +static INLINE void tcache_set(const struct timeval *const tv, + struct tm *const tm) { + unsigned stale = TCACHE_STALE; + if (__atomic_compare_exchange_n(&g_tcache_mode, &stale, TCACHE_FLUID, 0, + __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) { + g_tcache_tv = *tv; + g_tcache_tm = *tm; + __atomic_and_fetch(&g_tcache_mode, ~TCACHE_FLUID, __ATOMIC_RELEASE); + } +} +#endif + +static void time_callback(struct tm *const tm, unsigned *const msec) { +#if !_TRANSPORT_LOG_MESSAGE_FORMAT_DATETIME_USED + VAR_UNUSED(tm); + VAR_UNUSED(msec); +#else +#if defined(_WIN32) || defined(_WIN64) + SYSTEMTIME st; + GetLocalTime(&st); + tm->tm_year = st.wYear; + tm->tm_mon = st.wMonth - 1; + tm->tm_mday = st.wDay; + tm->tm_wday = st.wDayOfWeek; + tm->tm_hour = st.wHour; + tm->tm_min = st.wMinute; + tm->tm_sec = st.wSecond; + *msec = st.wMilliseconds; +#else + struct timeval tv; + gettimeofday(&tv, 0); +#ifndef TCACHE + localtime_r(&tv.tv_sec, tm); +#else + if (!tcache_get(&tv, tm)) { + localtime_r(&tv.tv_sec, tm); + tcache_set(&tv, tm); + } +#endif + *msec = (unsigned)tv.tv_usec / 1000; +#endif +#endif +} + +static void pid_callback(int *const pid, int *const tid) { +#if !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(PID, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) + VAR_UNUSED(pid); +#else +#if defined(_WIN32) || defined(_WIN64) + *pid = GetCurrentProcessId(); +#else + *pid = getpid(); +#endif +#endif + +#if !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(TID, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) + VAR_UNUSED(tid); +#else +#if defined(_WIN32) || defined(_WIN64) + *tid = GetCurrentThreadId(); +#elif defined(__ANDROID__) + *tid = gettid(); +#elif defined(__linux__) + *tid = syscall(SYS_gettid); +#elif defined(__MACH__) + *tid = (int)pthread_mach_thread_np(pthread_self()); +#else +#define Platform not supported +#endif +#endif +} + +static void buffer_callback(transport_log_message *msg, char *buf) { + msg->e = (msg->p = msg->buf = buf) + g_buf_sz; +} + +#if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(FUNCTION, \ + TRANSPORT_LOG_MESSAGE_SRC_FORMAT) +static const char *funcname(const char *func) { return func ? func : ""; } +#endif + +#if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(FILENAME, \ + TRANSPORT_LOG_MESSAGE_SRC_FORMAT) +static const char *filename(const char *file) { + const char *f = file; + for (const char *p = file; 0 != *p; ++p) { + if ('/' == *p || '\\' == *p) { + f = p + 1; + } + } + return f; +} +#endif + +static INLINE size_t nprintf_size(transport_log_message *const msg) { + // *nprintf() always puts 0 in the end when input buffer is not empty. This + // 0 is not desired because its presence sets (ctx->p) to (ctx->e - 1) which + // leaves space for one more character. Some put_xxx() functions don't use + // *nprintf() and could use that last character. In that case log line will + // have multiple (two) half-written parts which is confusing. To workaround + // that we allow *nprintf() to write its 0 in the eol area (which is always + // not empty). + return (size_t)(msg->e - msg->p + 1); +} + +static INLINE void put_nprintf(transport_log_message *const msg, const int n) { + if (0 < n) { + msg->p = n < msg->e - msg->p ? msg->p + n : msg->e; + } +} + +static INLINE char *put_padding_r(const unsigned w, const char wc, char *p, + char *e) { + for (char *const b = e - w; b < p; *--p = wc) { + } + return p; +} + +static char *put_integer_r(unsigned v, const int sign, const unsigned w, + const char wc, char *const e) { + static const char _signs[] = {'-', '0', '+'}; + static const char *const signs = _signs + 1; + char *p = e; + do { + *--p = '0' + v % 10; + } while (0 != (v /= 10)); + if (0 == sign) return put_padding_r(w, wc, p, e); + if ('0' != wc) { + *--p = signs[sign]; + return put_padding_r(w, wc, p, e); + } + p = put_padding_r(w, wc, p, e + 1); + *--p = signs[sign]; + return p; +} + +static INLINE char *put_uint_r(const unsigned v, const unsigned w, + const char wc, char *const e) { + return put_integer_r(v, 0, w, wc, e); +} + +static INLINE char *put_int_r(const int v, const unsigned w, const char wc, + char *const e) { + return 0 <= v ? put_integer_r((unsigned)v, 0, w, wc, e) + : put_integer_r((unsigned)-v, -1, w, wc, e); +} + +static INLINE char *put_stringn(const char *const s_p, const char *const s_e, + char *const p, char *const e) { + const ptrdiff_t m = e - p; + ptrdiff_t n = s_e - s_p; + if (n > m) { + n = m; + } + memcpy(p, s_p, n); + return p + n; +} + +static INLINE char *put_string(const char *s, char *p, char *const e) { + const ptrdiff_t n = e - p; + char *const c = (char *)memccpy(p, s, '\0', n); + return 0 != c ? c - 1 : e; +} + +static INLINE char *put_uint(unsigned v, const unsigned w, const char wc, + char *const p, char *const e) { + char buf[16]; + char *const se = buf + _countof(buf); + char *sp = put_uint_r(v, w, wc, se); + return put_stringn(sp, se, p, e); +} + +#define PUT_CSTR_R(p, STR) \ + do { \ + for (unsigned i = sizeof(STR) - 1; 0 < i--;) { \ + *--(p) = (STR)[i]; \ + } \ + } \ + _TRANSPORT_LOG_ONCE + +#define PUT_CSTR_CHECKED(p, e, STR) \ + do { \ + for (unsigned i = 0; (e) > (p) && (sizeof(STR) - 1) > i; ++i) { \ + *(p)++ = (STR)[i]; \ + } \ + } \ + _TRANSPORT_LOG_ONCE + +/* F_INIT field support. + */ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__YEAR +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__MONTH +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__DAY +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__HOUR +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__MINUTE +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__SECOND +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__MILLISECOND +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__PID +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__TID +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__LEVEL +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__TAG(ps, ts) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__FUNCTION +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__FILENAME +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__FILELINE +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__S(s) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__F_INIT(expr) _PP_UNTUPLE(expr); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__F_UINT(w, v) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT(field) \ + _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_INIT_, _, field) + +/* Implements generation of printf-like format string for log message + * format specification. + */ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__ "" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__YEAR "%04u" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MONTH "%02u" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__DAY "%02u" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__HOUR "%02u" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MINUTE "%02u" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__SECOND "%02u" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MILLISECOND "%03u" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__PID "%5i" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__TID "%5i" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__LEVEL "%c" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__TAG UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FUNCTION "%s" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FILENAME "%s" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FILELINE "%u" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__S(s) s +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__F_INIT(expr) "" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__F_UINT(w, v) "%" #w "u" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT(field) \ + _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT_, _, field) + +/* Implements generation of printf-like format parameters for log message + * format specification. + */ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__YEAR \ + , (unsigned)(tm.tm_year + 1900) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MONTH \ + , (unsigned)(tm.tm_mon + 1) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__DAY , (unsigned)tm.tm_mday +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__HOUR , (unsigned)tm.tm_hour +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MINUTE , (unsigned)tm.tm_min +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__SECOND , (unsigned)tm.tm_sec +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MILLISECOND , (unsigned)msec +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__PID , pid +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__TID , tid +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__LEVEL \ + , (char)lvl_char(msg->lvl) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__TAG UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FUNCTION , funcname(src->func) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FILENAME , filename(src->file) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FILELINE , src->line +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__S(s) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__F_INIT(expr) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__F_UINT(w, v) , v +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL(field) \ + _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL_, _, field) + +/* Implements generation of put_xxx_t statements for log message specification. + */ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__YEAR \ + p = put_uint_r(tm.tm_year + 1900, 4, '0', p); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__MONTH \ + p = put_uint_r((unsigned)tm.tm_mon + 1, 2, '0', p); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__DAY \ + p = put_uint_r((unsigned)tm.tm_mday, 2, '0', p); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__HOUR \ + p = put_uint_r((unsigned)tm.tm_hour, 2, '0', p); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__MINUTE \ + p = put_uint_r((unsigned)tm.tm_min, 2, '0', p); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__SECOND \ + p = put_uint_r((unsigned)tm.tm_sec, 2, '0', p); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__MILLISECOND \ + p = put_uint_r(msec, 3, '0', p); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__PID p = put_int_r(pid, 5, ' ', p); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__TID p = put_int_r(tid, 5, ' ', p); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__LEVEL *--p = lvl_char(msg->lvl); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__TAG UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__FUNCTION UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__FILENAME UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__FILELINE UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__S(s) PUT_CSTR_R(p, s); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__F_INIT(expr) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__F_UINT(w, v) \ + p = put_uint_r(v, w, ' ', p); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R(field) \ + _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R_, _, field) + +static void put_ctx(transport_log_message *const msg) { + _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_INIT, TRANSPORT_LOG_MESSAGE_CTX_FORMAT) +#if !_TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(TRANSPORT_LOG_MESSAGE_CTX_FORMAT) + VAR_UNUSED(msg); +#else +#if _TRANSPORT_LOG_MESSAGE_FORMAT_DATETIME_USED + struct tm tm; + unsigned msec; + g_time_cb(&tm, &msec); +#endif +#if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS( \ + PID, TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ + _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(TID, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) + int pid, tid; + g_pid_cb(&pid, &tid); +#endif + +#if TRANSPORT_LOG_OPTIMIZE_SIZE + int n; + n = snprintf(msg->p, nprintf_size(msg), + _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT, + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) + _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL, + TRANSPORT_LOG_MESSAGE_CTX_FORMAT)); + put_nprintf(msg, n); +#else + char buf[64]; + char *const e = buf + sizeof(buf); + char *p = e; + _PP_RMAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R, + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) + msg->p = put_stringn(p, e, msg->p, msg->e); +#endif +#endif +} + +#define PUT_TAG(msg, tag, prefix_delim, tag_delim) \ + do { \ + const char *ch; \ + msg->tag_b = msg->p; \ + if (0 != (ch = _transport_log_tag_prefix)) { \ + for (; msg->e != msg->p && 0 != (*msg->p = *ch); ++msg->p, ++ch) { \ + } \ + } \ + if (0 != (ch = tag) && 0 != tag[0]) { \ + if (msg->tag_b != msg->p) { \ + PUT_CSTR_CHECKED(msg->p, msg->e, prefix_delim); \ + } \ + for (; msg->e != msg->p && 0 != (*msg->p = *ch); ++msg->p, ++ch) { \ + } \ + } \ + msg->tag_e = msg->p; \ + if (msg->tag_b != msg->p) { \ + PUT_CSTR_CHECKED(msg->p, msg->e, tag_delim); \ + } \ + } \ + _TRANSPORT_LOG_ONCE + +/* Implements simple put statements for log message specification. + */ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__YEAR UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__MONTH UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__DAY UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__HOUR UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__MINUTE UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__SECOND UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__MILLISECOND UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__PID UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__TID UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__LEVEL UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__TAG(pd, td) \ + PUT_TAG(msg, tag, pd, td); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__FUNCTION \ + msg->p = put_string(funcname(src->func), msg->p, msg->e); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__FILENAME \ + msg->p = put_string(filename(src->file), msg->p, msg->e); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__FILELINE \ + msg->p = put_uint(src->line, 0, '\0', msg->p, msg->e); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__S(s) \ + PUT_CSTR_CHECKED(msg->p, msg->e, s); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__F_INIT(expr) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__F_UINT(w, v) \ + msg->p = put_uint(v, w, ' ', msg->p, msg->e); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT(field) \ + _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT_, _, field) + +static void put_tag(transport_log_message *const msg, const char *const tag) { + _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_INIT, TRANSPORT_LOG_MESSAGE_TAG_FORMAT) +#if !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(TAG, \ + TRANSPORT_LOG_MESSAGE_TAG_FORMAT) + VAR_UNUSED(tag); +#endif +#if !_TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(TRANSPORT_LOG_MESSAGE_TAG_FORMAT) + VAR_UNUSED(msg); +#else + _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT, TRANSPORT_LOG_MESSAGE_TAG_FORMAT) +#endif +} + +static void put_src(transport_log_message *const msg, + const src_location *const src) { + _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_INIT, TRANSPORT_LOG_MESSAGE_SRC_FORMAT) +#if !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS( \ + FUNCTION, TRANSPORT_LOG_MESSAGE_SRC_FORMAT) && \ + !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS( \ + FILENAME, TRANSPORT_LOG_MESSAGE_SRC_FORMAT) && \ + !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(FILELINE, \ + TRANSPORT_LOG_MESSAGE_SRC_FORMAT) + VAR_UNUSED(src); +#endif +#if !_TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(TRANSPORT_LOG_MESSAGE_SRC_FORMAT) + VAR_UNUSED(msg); +#else +#if TRANSPORT_LOG_OPTIMIZE_SIZE + int n; + n = snprintf(msg->p, nprintf_size(msg), + _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT, + TRANSPORT_LOG_MESSAGE_SRC_FORMAT) + _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL, + TRANSPORT_LOG_MESSAGE_SRC_FORMAT)); + put_nprintf(msg, n); +#else + _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT, TRANSPORT_LOG_MESSAGE_SRC_FORMAT) +#endif +#endif +} + +static void put_msg(transport_log_message *const msg, const char *const fmt, + va_list va) { + int n; + msg->msg_b = msg->p; + n = vsnprintf(msg->p, nprintf_size(msg), fmt, va); + put_nprintf(msg, n); +} + +static void output_mem(const transport_log_spec *log, + transport_log_message *const msg, + const mem_block *const mem) { + if (0 == mem->d || 0 == mem->d_sz) { + return; + } + const unsigned char *mem_p = (const unsigned char *)mem->d; + const unsigned char *const mem_e = mem_p + mem->d_sz; + const unsigned char *mem_cut; + const ptrdiff_t mem_width = (ptrdiff_t)log->format->mem_width; + char *const hex_b = msg->msg_b; + char *const ascii_b = hex_b + 2 * mem_width + 2; + char *const ascii_e = ascii_b + mem_width; + if (msg->e < ascii_e) { + return; + } + while (mem_p != mem_e) { + char *hex = hex_b; + char *ascii = ascii_b; + for (mem_cut = mem_width < mem_e - mem_p ? mem_p + mem_width : mem_e; + mem_cut != mem_p; ++mem_p) { + const unsigned char ch = *mem_p; + *hex++ = c_hex[(0xf0 & ch) >> 4]; + *hex++ = c_hex[(0x0f & ch)]; + *ascii++ = isprint(ch) ? (char)ch : '?'; + } + while (hex != ascii_b) { + *hex++ = ' '; + } + msg->p = ascii; + log->output->callback(msg, log->output->arg); + } +} + +void transport_log_set_tag_prefix(const char *const prefix) { + _transport_log_tag_prefix = prefix; +} + +void transport_log_set_mem_width(const unsigned w) { + _transport_log_global_format.mem_width = w; +} + +void transport_log_set_output_level(const int lvl) { + _transport_log_global_output_lvl = lvl; +} + +void transport_log_set_output_v(const unsigned mask, void *const arg, + const transport_log_output_cb callback) { + _transport_log_global_output.mask = mask; + _transport_log_global_output.arg = arg; + _transport_log_global_output.callback = callback; +} + +static void _transport_log_write_imp(const transport_log_spec *log, + const src_location *const src, + const mem_block *const mem, const int lvl, + const char *const tag, + const char *const fmt, va_list va) { + transport_log_message msg; + char buf[TRANSPORT_LOG_BUF_SZ]; + const unsigned mask = log->output->mask; + msg.lvl = lvl; + msg.tag = tag; + g_buffer_cb(&msg, buf); + if (TRANSPORT_LOG_PUT_CTX & mask) { + put_ctx(&msg); + } + if (TRANSPORT_LOG_PUT_TAG & mask) { + put_tag(&msg, tag); + } + if (0 != src && TRANSPORT_LOG_PUT_SRC & mask) { + put_src(&msg, src); + } + if (TRANSPORT_LOG_PUT_MSG & mask) { + put_msg(&msg, fmt, va); + } + log->output->callback(&msg, log->output->arg); + if (0 != mem && TRANSPORT_LOG_PUT_MSG & mask) { + output_mem(log, &msg, mem); + } +} + +void _transport_log_write_d(const char *const func, const char *const file, + const unsigned line, const int lvl, + const char *const tag, const char *const fmt, ...) { + const src_location src = {func, file, line}; + va_list va; + va_start(va, fmt); + _transport_log_write_imp(&global_spec, &src, 0, lvl, tag, fmt, va); + va_end(va); +} + +void _transport_log_write_aux_d(const char *const func, const char *const file, + const unsigned line, + const transport_log_spec *const log, + const int lvl, const char *const tag, + const char *const fmt, ...) { + const src_location src = {func, file, line}; + va_list va; + va_start(va, fmt); + _transport_log_write_imp(log, &src, 0, lvl, tag, fmt, va); + va_end(va); +} + +void _transport_log_write(const int lvl, const char *const tag, + const char *const fmt, ...) { + va_list va; + va_start(va, fmt); + _transport_log_write_imp(&global_spec, 0, 0, lvl, tag, fmt, va); + va_end(va); +} + +void _transport_log_write_aux(const transport_log_spec *const log, + const int lvl, const char *const tag, + const char *const fmt, ...) { + va_list va; + va_start(va, fmt); + _transport_log_write_imp(log, 0, 0, lvl, tag, fmt, va); + va_end(va); +} + +void _transport_log_write_mem_d(const char *const func, const char *const file, + const unsigned line, const int lvl, + const char *const tag, const void *const d, + const unsigned d_sz, const char *const fmt, + ...) { + const src_location src = {func, file, line}; + const mem_block mem = {d, d_sz}; + va_list va; + va_start(va, fmt); + _transport_log_write_imp(&global_spec, &src, &mem, lvl, tag, fmt, va); + va_end(va); +} + +void _transport_log_write_mem_aux_d(const char *const func, + const char *const file, const unsigned line, + const transport_log_spec *const log, + const int lvl, const char *const tag, + const void *const d, const unsigned d_sz, + const char *const fmt, ...) { + const src_location src = {func, file, line}; + const mem_block mem = {d, d_sz}; + va_list va; + va_start(va, fmt); + _transport_log_write_imp(log, &src, &mem, lvl, tag, fmt, va); + va_end(va); +} + +void _transport_log_write_mem(const int lvl, const char *const tag, + const void *const d, const unsigned d_sz, + const char *const fmt, ...) { + const mem_block mem = {d, d_sz}; + va_list va; + va_start(va, fmt); + _transport_log_write_imp(&global_spec, 0, &mem, lvl, tag, fmt, va); + va_end(va); +} + +void _transport_log_write_mem_aux(const transport_log_spec *const log, + const int lvl, const char *const tag, + const void *const d, const unsigned d_sz, + const char *const fmt, ...) { + const mem_block mem = {d, d_sz}; + va_list va; + va_start(va, fmt); + _transport_log_write_imp(log, 0, &mem, lvl, tag, fmt, va); + va_end(va); +} \ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/log.h b/libtransport/src/hicn/transport/utils/log.h new file mode 100755 index 000000000..17e47e7df --- /dev/null +++ b/libtransport/src/hicn/transport/utils/log.h @@ -0,0 +1,1057 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 wonder-mice + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +/* To detect incompatible changes you can define TRANSPORT_LOG_VERSION_REQUIRED + * to be the current value of TRANSPORT_LOG_VERSION before including this file + * (or via compiler command line): + * + * #define TRANSPORT_LOG_VERSION_REQUIRED 4 + * #include + * + * Compilation will fail when included file has different version. + */ +#define TRANSPORT_LOG_VERSION 4 +#if defined(TRANSPORT_LOG_VERSION_REQUIRED) +#if TRANSPORT_LOG_VERSION_REQUIRED != TRANSPORT_LOG_VERSION +#error different transport_log version required +#endif +#endif + +/* Log level guideline: + * - TRANSPORT_LOG_FATAL - happened something impossible and absolutely + * unexpected. Process can't continue and must be terminated. Example: division + * by zero, unexpected modifications from other thread. + * - TRANSPORT_LOG_ERROR - happened something possible, but highly unexpected. + * The process is able to recover and continue execution. Example: out of memory + * (could also be FATAL if not handled properly). + * - TRANSPORT_LOG_WARN - happened something that *usually* should not happen + * and significantly changes application behavior for some period of time. + * Example: configuration file not found, auth error. + * - TRANSPORT_LOG_INFO - happened significant life cycle event or major state + * transition. + * Example: app started, user logged in. + * - TRANSPORT_LOG_DEBUG - minimal set of events that could help to reconstruct + * the execution path. Usually disabled in release builds. + * - TRANSPORT_LOG_VERBOSE - all other events. Usually disabled in release + * builds. + * + * *Ideally*, log file of debugged, well tested, production ready application + * should be empty or very small. Choosing a right log level is as important as + * providing short and self descriptive log message. + */ +#define TRANSPORT_LOG_VERBOSE 1 +#define TRANSPORT_LOG_DEBUG 2 +#define TRANSPORT_LOG_INFO 3 +#define TRANSPORT_LOG_WARN 4 +#define TRANSPORT_LOG_ERROR 5 +#define TRANSPORT_LOG_FATAL 6 +#define TRANSPORT_LOG_NONE 0xFF + +/* "Current" log level is a compile time check and has no runtime overhead. Log + * level that is below current log level it said to be "disabled". Otherwise, + * it's "enabled". Log messages that are disabled has no runtime overhead - they + * are converted to no-op by preprocessor and then eliminated by compiler. + * Current log level is configured per compilation module (.c/.cpp/.m file) by + * defining TRANSPORT_LOG_DEF_LEVEL or TRANSPORT_LOG_LEVEL. TRANSPORT_LOG_LEVEL + * has higer priority and when defined overrides value provided by + * TRANSPORT_LOG_DEF_LEVEL. + * + * Common practice is to define default current log level with + * TRANSPORT_LOG_DEF_LEVEL in build script (e.g. Makefile, CMakeLists.txt, gyp, + * etc.) for the entire project or target: + * + * CC_ARGS := -DTRANSPORT_LOG_DEF_LEVEL=TRANSPORT_LOG_INFO + * + * And when necessary to override it with TRANSPORT_LOG_LEVEL in .c/.cpp/.m + * files before including transport_log.h: + * + * #define TRANSPORT_LOG_LEVEL TRANSPORT_LOG_VERBOSE + * #include + * + * If both TRANSPORT_LOG_DEF_LEVEL and TRANSPORT_LOG_LEVEL are undefined, then + * TRANSPORT_LOG_INFO will be used for release builds (NDEBUG is defined) and + * TRANSPORT_LOG_DEBUG otherwise (NDEBUG is not defined). + */ +#if defined(TRANSPORT_LOG_LEVEL) +#define _TRANSPORT_LOG_LEVEL TRANSPORT_LOG_LEVEL +#elif defined(TRANSPORT_LOG_DEF_LEVEL) +#define _TRANSPORT_LOG_LEVEL TRANSPORT_LOG_DEF_LEVEL +#else +#ifdef NDEBUG +#define _TRANSPORT_LOG_LEVEL TRANSPORT_LOG_INFO +#else +#define _TRANSPORT_LOG_LEVEL TRANSPORT_LOG_DEBUG +#endif +#endif + +/* "Output" log level is a runtime check. When log level is below output log + * level it said to be "turned off" (or just "off" for short). Otherwise it's + * "turned on" (or just "on"). Log levels that were "disabled" (see + * TRANSPORT_LOG_LEVEL and TRANSPORT_LOG_DEF_LEVEL) can't be "turned on", but + * "enabled" log levels could be "turned off". Only messages with log level + * which is "turned on" will reach output facility. All other messages will be + * ignored (and their arguments will not be evaluated). Output log level is a + * global property and configured per process using + * transport_log_set_output_level() function which can be called at any time. + * + * Though in some cases it could be useful to configure output log level per + * compilation module or per library. There are two ways to achieve that: + * - Define TRANSPORT_LOG_OUTPUT_LEVEL to expresion that evaluates to desired + * output log level. + * - Copy transport_log.h and transport_log.c files into your library and build + * it with TRANSPORT_LOG_LIBRARY_PREFIX defined to library specific prefix. See + * TRANSPORT_LOG_LIBRARY_PREFIX for more details. + * + * When defined, TRANSPORT_LOG_OUTPUT_LEVEL must evaluate to integral value that + * corresponds to desired output log level. Use it only when compilation module + * is required to have output log level which is different from global output + * log level set by transport_log_set_output_level() function. For other cases, + * consider defining TRANSPORT_LOG_LEVEL or using + * transport_log_set_output_level() function. + * + * Example: + * + * #define TRANSPORT_LOG_OUTPUT_LEVEL g_module_log_level + * #include + * static int g_module_log_level = TRANSPORT_LOG_INFO; + * static void foo() { + * TRANSPORT_LOGI("Will check g_module_log_level for output log level"); + * } + * void debug_log(bool on) { + * g_module_log_level = on? TRANSPORT_LOG_DEBUG: TRANSPORT_LOG_INFO; + * } + * + * Note on performance. This expression will be evaluated each time message is + * logged (except when message log level is "disabled" - see TRANSPORT_LOG_LEVEL + * for details). Keep this expression as simple as possible, otherwise it will + * not only add runtime overhead, but also will increase size of call site + * (which will result in larger executable). The prefered way is to use integer + * variable (as in example above). If structure must be used, log_level field + * must be the first field in this structure: + * + * #define TRANSPORT_LOG_OUTPUT_LEVEL (g_config.log_level) + * #include + * struct config { + * int log_level; + * unsigned other_field; + * [...] + * }; + * static config g_config = {TRANSPORT_LOG_INFO, 0, ...}; + * + * This allows compiler to generate more compact load instruction (no need to + * specify offset since it's zero). Calling a function to get output log level + * is generaly a bad idea, since it will increase call site size and runtime + * overhead even further. + */ +#if defined(TRANSPORT_LOG_OUTPUT_LEVEL) +#define _TRANSPORT_LOG_OUTPUT_LEVEL TRANSPORT_LOG_OUTPUT_LEVEL +#else +#define _TRANSPORT_LOG_OUTPUT_LEVEL _transport_log_global_output_lvl +#endif + +/* "Tag" is a compound string that could be associated with a log message. It + * consists of tag prefix and tag (both are optional). + * + * Tag prefix is a global property and configured per process using + * transport_log_set_tag_prefix() function. Tag prefix identifies context in + * which component or module is running (e.g. process name). For example, the + * same library could be used in both client and server processes that work on + * the same machine. Tag prefix could be used to easily distinguish between + * them. For more details about tag prefix see transport_log_set_tag_prefix() + * function. Tag prefix + * + * Tag identifies component or module. It is configured per compilation module + * (.c/.cpp/.m file) by defining TRANSPORT_LOG_TAG or TRANSPORT_LOG_DEF_TAG. + * TRANSPORT_LOG_TAG has higer priority and when defined overrides value + * provided by TRANSPORT_LOG_DEF_TAG. When defined, value must evaluate to + * (const char *), so for strings double quotes must be used. + * + * Default tag could be defined with TRANSPORT_LOG_DEF_TAG in build script (e.g. + * Makefile, CMakeLists.txt, gyp, etc.) for the entire project or target: + * + * CC_ARGS := -DTRANSPORT_LOG_DEF_TAG=\"MISC\" + * + * And when necessary could be overriden with TRANSPORT_LOG_TAG in .c/.cpp/.m + * files before including transport_log.h: + * + * #define TRANSPORT_LOG_TAG "MAIN" + * #include + * + * If both TRANSPORT_LOG_DEF_TAG and TRANSPORT_LOG_TAG are undefined no tag will + * be added to the log message (tag prefix still could be added though). + * + * Output example: + * + * 04-29 22:43:20.244 40059 1299 I hello.MAIN Number of arguments: 1 + * | | + * | +- tag (e.g. module) + * +- tag prefix (e.g. process name) + */ +#if defined(TRANSPORT_LOG_TAG) +#define _TRANSPORT_LOG_TAG TRANSPORT_LOG_TAG +#elif defined(TRANSPORT_LOG_DEF_TAG) +#define _TRANSPORT_LOG_TAG TRANSPORT_LOG_DEF_TAG +#else +#define _TRANSPORT_LOG_TAG 0 +#endif + +/* Source location is part of a log line that describes location (function or + * method name, file name and line number, e.g. "runloop@main.cpp:68") of a + * log statement that produced it. + * Source location formats are: + * - TRANSPORT_LOG_SRCLOC_NONE - don't add source location to log line. + * - TRANSPORT_LOG_SRCLOC_SHORT - add source location in short form (file and + * line number, e.g. "@main.cpp:68"). + * - TRANSPORT_LOG_SRCLOC_LONG - add source location in long form (function or + * method name, file and line number, e.g. "runloop@main.cpp:68"). + */ +#define TRANSPORT_LOG_SRCLOC_NONE 0 +#define TRANSPORT_LOG_SRCLOC_SHORT 1 +#define TRANSPORT_LOG_SRCLOC_LONG 2 + +/* Source location format is configured per compilation module (.c/.cpp/.m + * file) by defining TRANSPORT_LOG_DEF_SRCLOC or TRANSPORT_LOG_SRCLOC. + * TRANSPORT_LOG_SRCLOC has higer priority and when defined overrides value + * provided by TRANSPORT_LOG_DEF_SRCLOC. + * + * Common practice is to define default format with TRANSPORT_LOG_DEF_SRCLOC in + * build script (e.g. Makefile, CMakeLists.txt, gyp, etc.) for the entire + * project or target: + * + * CC_ARGS := -DTRANSPORT_LOG_DEF_SRCLOC=TRANSPORT_LOG_SRCLOC_LONG + * + * And when necessary to override it with TRANSPORT_LOG_SRCLOC in .c/.cpp/.m + * files before including transport_log.h: + * + * #define TRANSPORT_LOG_SRCLOC TRANSPORT_LOG_SRCLOC_NONE + * #include + * + * If both TRANSPORT_LOG_DEF_SRCLOC and TRANSPORT_LOG_SRCLOC are undefined, then + * TRANSPORT_LOG_SRCLOC_NONE will be used for release builds (NDEBUG is defined) + * and TRANSPORT_LOG_SRCLOC_LONG otherwise (NDEBUG is not defined). + */ +#if defined(TRANSPORT_LOG_SRCLOC) +#define _TRANSPORT_LOG_SRCLOC TRANSPORT_LOG_SRCLOC +#elif defined(TRANSPORT_LOG_DEF_SRCLOC) +#define _TRANSPORT_LOG_SRCLOC TRANSPORT_LOG_DEF_SRCLOC +#else +#ifdef NDEBUG +#define _TRANSPORT_LOG_SRCLOC TRANSPORT_LOG_SRCLOC_NONE +#else +#define _TRANSPORT_LOG_SRCLOC TRANSPORT_LOG_SRCLOC_LONG +#endif +#endif +#if TRANSPORT_LOG_SRCLOC_LONG == _TRANSPORT_LOG_SRCLOC +#define _TRANSPORT_LOG_SRCLOC_FUNCTION _TRANSPORT_LOG_FUNCTION +#else +#define _TRANSPORT_LOG_SRCLOC_FUNCTION 0 +#endif + +/* Censoring provides conditional logging of secret information, also known as + * Personally Identifiable Information (PII) or Sensitive Personal Information + * (SPI). Censoring can be either enabled (TRANSPORT_LOG_CENSORED) or disabled + * (TRANSPORT_LOG_UNCENSORED). When censoring is enabled, log statements marked + * as "secrets" will be ignored and will have zero overhead (arguments also will + * not be evaluated). + */ +#define TRANSPORT_LOG_CENSORED 1 +#define TRANSPORT_LOG_UNCENSORED 0 + +/* Censoring is configured per compilation module (.c/.cpp/.m file) by defining + * TRANSPORT_LOG_DEF_CENSORING or TRANSPORT_LOG_CENSORING. + * TRANSPORT_LOG_CENSORING has higer priority and when defined overrides value + * provided by TRANSPORT_LOG_DEF_CENSORING. + * + * Common practice is to define default censoring with + * TRANSPORT_LOG_DEF_CENSORING in build script (e.g. Makefile, CMakeLists.txt, + * gyp, etc.) for the entire project or target: + * + * CC_ARGS := -DTRANSPORT_LOG_DEF_CENSORING=TRANSPORT_LOG_CENSORED + * + * And when necessary to override it with TRANSPORT_LOG_CENSORING in .c/.cpp/.m + * files before including transport_log.h (consider doing it only for debug + * purposes and be very careful not to push such temporary changes to source + * control): + * + * #define TRANSPORT_LOG_CENSORING TRANSPORT_LOG_UNCENSORED + * #include + * + * If both TRANSPORT_LOG_DEF_CENSORING and TRANSPORT_LOG_CENSORING are + * undefined, then TRANSPORT_LOG_CENSORED will be used for release builds + * (NDEBUG is defined) and TRANSPORT_LOG_UNCENSORED otherwise (NDEBUG is not + * defined). + */ +#if defined(TRANSPORT_LOG_CENSORING) +#define _TRANSPORT_LOG_CENSORING TRANSPORT_LOG_CENSORING +#elif defined(TRANSPORT_LOG_DEF_CENSORING) +#define _TRANSPORT_LOG_CENSORING TRANSPORT_LOG_DEF_CENSORING +#else +#ifdef NDEBUG +#define _TRANSPORT_LOG_CENSORING TRANSPORT_LOG_CENSORED +#else +#define _TRANSPORT_LOG_CENSORING TRANSPORT_LOG_UNCENSORED +#endif +#endif + +/* Check censoring at compile time. Evaluates to true when censoring is disabled + * (i.e. when secrets will be logged). For example: + * + * #if TRANSPORT_LOG_SECRETS + * char ssn[16]; + * getSocialSecurityNumber(ssn); + * TRANSPORT_LOGI("Customer ssn: %s", ssn); + * #endif + * + * See TRANSPORT_LOG_SECRET() macro for a more convenient way of guarding single + * log statement. + */ +#define TRANSPORT_LOG_SECRETS \ + (TRANSPORT_LOG_UNCENSORED == _TRANSPORT_LOG_CENSORING) + +/* Static (compile-time) initialization support allows to configure logging + * before entering main() function. This mostly useful in C++ where functions + * and methods could be called during initialization of global objects. Those + * functions and methods could record log messages too and for that reason + * static initialization of logging configuration is customizable. + * + * Macros below allow to specify values to use for initial configuration: + * - TRANSPORT_LOG_EXTERN_TAG_PREFIX - tag prefix (default: none) + * - TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT - global format options (default: see + * TRANSPORT_LOG_MEM_WIDTH in transport_log.c) + * - TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT - global output facility (default: + * stderr or platform specific, see TRANSPORT_LOG_USE_XXX macros in + * transport_log.c) + * - TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL - global output log level + * (default: 0 - all levals are "turned on") + * + * For example, in log_config.c: + * + * #include + * TRANSPORT_LOG_DEFINE_TAG_PREFIX = "MyApp"; + * TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT = {CUSTOM_MEM_WIDTH}; + * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {TRANSPORT_LOG_PUT_STD, + * custom_output_callback, 0}; TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = + * TRANSPORT_LOG_INFO; + * + * However, to use any of those macros transport_log library must be compiled + * with following macros defined: + * - to use TRANSPORT_LOG_DEFINE_TAG_PREFIX define + * TRANSPORT_LOG_EXTERN_TAG_PREFIX + * - to use TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT define + * TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT + * - to use TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT define + * TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT + * - to use TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL define + * TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL + * + * When transport_log library compiled with one of TRANSPORT_LOG_EXTERN_XXX + * macros defined, corresponding TRANSPORT_LOG_DEFINE_XXX macro MUST be used + * exactly once somewhere. Otherwise build will fail with link error (undefined + * symbol). + */ +#define TRANSPORT_LOG_DEFINE_TAG_PREFIX const char *_transport_log_tag_prefix +#define TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT \ + transport_log_format _transport_log_global_format +#define TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT \ + transport_log_output _transport_log_global_output +#define TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL \ + int _transport_log_global_output_lvl + +/* Pointer to global format options. Direct modification is not allowed. Use + * transport_log_set_mem_width() instead. Could be used to initialize + * transport_log_spec structure: + * + * const transport_log_output g_output = {TRANSPORT_LOG_PUT_STD, + * output_callback, 0}; const transport_log_spec g_spec = + * {TRANSPORT_LOG_GLOBAL_FORMAT, &g_output}; TRANSPORT_LOGI_AUX(&g_spec, + * "Hello"); + */ +#define TRANSPORT_LOG_GLOBAL_FORMAT \ + ((const transport_log_format *)&_transport_log_global_format) + +/* Pointer to global output variable. Direct modification is not allowed. Use + * transport_log_set_output_v() or transport_log_set_output_p() instead. Could + * be used to initialize transport_log_spec structure: + * + * const transport_log_format g_format = {40}; + * const transport_log_spec g_spec = {g_format, TRANSPORT_LOG_GLOBAL_OUTPUT}; + * TRANSPORT_LOGI_AUX(&g_spec, "Hello"); + */ +#define TRANSPORT_LOG_GLOBAL_OUTPUT \ + ((const transport_log_output *)&_transport_log_global_output) + +/* When defined, all library symbols produced by linker will be prefixed with + * provided value. That allows to use transport_log library privately in another + * libraries without exposing transport_log symbols in their original form (to + * avoid possible conflicts with other libraries / components that also could + * use transport_log for logging). Value must be without quotes, for example: + * + * CC_ARGS := -DTRANSPORT_LOG_LIBRARY_PREFIX=my_lib_ + * + * Note, that in this mode TRANSPORT_LOG_LIBRARY_PREFIX must be defined when + * building transport_log library AND it also must be defined to the same value + * when building a library that uses it. For example, consider fictional + * KittyHttp library that wants to use transport_log for logging. First approach + * that could be taken is to add transport_log.h and transport_log.c to the + * KittyHttp's source code tree directly. In that case it will be enough just to + * define TRANSPORT_LOG_LIBRARY_PREFIX in KittyHttp's build script: + * + * // KittyHttp/CMakeLists.txt + * target_compile_definitions(KittyHttp PRIVATE + * "TRANSPORT_LOG_LIBRARY_PREFIX=KittyHttp_") + * + * If KittyHttp doesn't want to include transport_log source code in its source + * tree and wants to build transport_log as a separate library than + * transport_log library must be built with TRANSPORT_LOG_LIBRARY_PREFIX defined + * to KittyHttp_ AND KittyHttp library itself also needs to define + * TRANSPORT_LOG_LIBRARY_PREFIX to KittyHttp_. It can do so either in its build + * script, as in example above, or by providing a wrapper header that KittyHttp + * library will need to use instead of transport_log.h: + * + * // KittyHttpLogging.h + * #define TRANSPORT_LOG_LIBRARY_PREFIX KittyHttp_ + * #include + * + * Regardless of the method chosen, the end result is that transport_log symbols + * will be prefixed with "KittyHttp_", so if a user of KittyHttp (say + * DogeBrowser) also uses transport_log for logging, they will not interferer + * with each other. Both will have their own log level, output facility, format + * options etc. + */ +#ifdef TRANSPORT_LOG_LIBRARY_PREFIX +#define _TRANSPORT_LOG_DECOR__(prefix, name) prefix##name +#define _TRANSPORT_LOG_DECOR_(prefix, name) _TRANSPORT_LOG_DECOR__(prefix, name) +#define _TRANSPORT_LOG_DECOR(name) \ + _TRANSPORT_LOG_DECOR_(TRANSPORT_LOG_LIBRARY_PREFIX, name) + +#define transport_log_set_tag_prefix \ + _TRANSPORT_LOG_DECOR(transport_log_set_tag_prefix) +#define transport_log_set_mem_width \ + _TRANSPORT_LOG_DECOR(transport_log_set_mem_width) +#define transport_log_set_output_level \ + _TRANSPORT_LOG_DECOR(transport_log_set_output_level) +#define transport_log_set_output_v \ + _TRANSPORT_LOG_DECOR(transport_log_set_output_v) +#define transport_log_set_output_p \ + _TRANSPORT_LOG_DECOR(transport_log_set_output_p) +#define transport_log_out_stderr_callback \ + _TRANSPORT_LOG_DECOR(transport_log_out_stderr_callback) +#define _transport_log_tag_prefix \ + _TRANSPORT_LOG_DECOR(_transport_log_tag_prefix) +#define _transport_log_global_format \ + _TRANSPORT_LOG_DECOR(_transport_log_global_format) +#define _transport_log_global_output \ + _TRANSPORT_LOG_DECOR(_transport_log_global_output) +#define _transport_log_global_output_lvl \ + _TRANSPORT_LOG_DECOR(_transport_log_global_output_lvl) +#define _transport_log_write_d _TRANSPORT_LOG_DECOR(_transport_log_write_d) +#define _transport_log_write_aux_d \ + _TRANSPORT_LOG_DECOR(_transport_log_write_aux_d) +#define _transport_log_write _TRANSPORT_LOG_DECOR(_transport_log_write) +#define _transport_log_write_aux _TRANSPORT_LOG_DECOR(_transport_log_write_aux) +#define _transport_log_write_mem_d \ + _TRANSPORT_LOG_DECOR(_transport_log_write_mem_d) +#define _transport_log_write_mem_aux_d \ + _TRANSPORT_LOG_DECOR(_transport_log_write_mem_aux_d) +#define _transport_log_write_mem _TRANSPORT_LOG_DECOR(_transport_log_write_mem) +#define _transport_log_write_mem_aux \ + _TRANSPORT_LOG_DECOR(_transport_log_write_mem_aux) +#define _transport_log_stderr_spec \ + _TRANSPORT_LOG_DECOR(_transport_log_stderr_spec) +#endif + +#if defined(__printflike) +#define _TRANSPORT_LOG_PRINTFLIKE(str_index, first_to_check) \ + __printflike(str_index, first_to_check) +#elif defined(__GNUC__) +#define _TRANSPORT_LOG_PRINTFLIKE(str_index, first_to_check) \ + __attribute__((format(__printf__, str_index, first_to_check))) +#else +#define _TRANSPORT_LOG_PRINTFLIKE(str_index, first_to_check) +#endif + +#if (defined(_WIN32) || defined(_WIN64)) && !defined(__GNUC__) +#define _TRANSPORT_LOG_FUNCTION __FUNCTION__ +#else +#define _TRANSPORT_LOG_FUNCTION __func__ +#endif + +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) +#define _TRANSPORT_LOG_INLINE __inline +#define _TRANSPORT_LOG_IF(cond) \ + __pragma(warning(push)) __pragma(warning(disable : 4127)) if (cond) \ + __pragma(warning(pop)) +#define _TRANSPORT_LOG_WHILE(cond) \ + __pragma(warning(push)) __pragma(warning(disable : 4127)) while (cond) \ + __pragma(warning(pop)) +#else +#define _TRANSPORT_LOG_INLINE inline +#define _TRANSPORT_LOG_IF(cond) if (cond) +#define _TRANSPORT_LOG_WHILE(cond) while (cond) +#endif +#define _TRANSPORT_LOG_NEVER _TRANSPORT_LOG_IF(0) +#define _TRANSPORT_LOG_ONCE _TRANSPORT_LOG_WHILE(0) + +#ifdef __cplusplus +extern "C" { +#endif + +/* Set tag prefix. Prefix will be separated from the tag with dot ('.'). + * Use 0 or empty string to disable (default). Common use is to set it to + * the process (or build target) name (e.g. to separate client and server + * processes). Function will NOT copy provided prefix string, but will store the + * pointer. Hence specified prefix string must remain valid. See + * TRANSPORT_LOG_DEFINE_TAG_PREFIX for a way to set it before entering main() + * function. See TRANSPORT_LOG_TAG for more information about tag and tag + * prefix. + */ +void transport_log_set_tag_prefix(const char *const prefix); + +/* Set number of bytes per log line in memory (ASCII-HEX) output. Example: + * + * I hello.MAIN 4c6f72656d20697073756d20646f6c6f Lorem ipsum dolo + * |<- w bytes ->| |<- w chars ->| + * + * See TRANSPORT_LOGF_MEM and TRANSPORT_LOGF_MEM_AUX for more details. + */ +void transport_log_set_mem_width(const unsigned w); + +/* Set "output" log level. See TRANSPORT_LOG_LEVEL and + * TRANSPORT_LOG_OUTPUT_LEVEL for more info about log levels. + */ +void transport_log_set_output_level(const int lvl); + +/* Put mask is a set of flags that define what fields will be added to each + * log message. Default value is TRANSPORT_LOG_PUT_STD and other flags could be + * used to alter its behavior. See transport_log_set_output_v() for more + * details. + * + * Note about TRANSPORT_LOG_PUT_SRC: it will be added only in debug builds + * (NDEBUG is not defined). + */ +enum { + TRANSPORT_LOG_PUT_CTX = 1 << 0, /* context (time, pid, tid, log level) */ + TRANSPORT_LOG_PUT_TAG = 1 << 1, /* tag (including tag prefix) */ + TRANSPORT_LOG_PUT_SRC = 1 << 2, /* source location (file, line, function) */ + TRANSPORT_LOG_PUT_MSG = 1 << 3, /* message text (formatted string) */ + TRANSPORT_LOG_PUT_STD = 0xffff, /* everything (default) */ +}; + +typedef struct transport_log_message { + int lvl; /* Log level of the message */ + const char *tag; /* Associated tag (without tag prefix) */ + char *buf; /* Buffer start */ + char *e; /* Buffer end (last position where EOL with 0 could be written) */ + char *p; /* Buffer content end (append position) */ + char *tag_b; /* Prefixed tag start */ + char *tag_e; /* Prefixed tag end (if != tag_b, points to msg separator) */ + char *msg_b; /* Message start (expanded format string) */ +} transport_log_message; + +/* Type of output callback function. It will be called for each log line allowed + * by both "current" and "output" log levels ("enabled" and "turned on"). + * Callback function is allowed to modify content of the buffers pointed by the + * msg, but it's not allowed to modify any of msg fields. Buffer pointed by msg + * is UTF-8 encoded (no BOM mark). + */ +typedef void (*transport_log_output_cb)(const transport_log_message *msg, + void *arg); + +/* Format options. For more details see transport_log_set_mem_width(). + */ +typedef struct transport_log_format { + unsigned mem_width; /* Bytes per line in memory (ASCII-HEX) dump */ +} transport_log_format; + +/* Output facility. + */ +typedef struct transport_log_output { + unsigned + mask; /* What to put into log line buffer (see TRANSPORT_LOG_PUT_XXX) */ + void *arg; /* User provided output callback argument */ + transport_log_output_cb callback; /* Output callback function */ +} transport_log_output; + +/* Set output callback function. + * + * Mask allows to control what information will be added to the log line buffer + * before callback function is invoked. Default mask value is + * TRANSPORT_LOG_PUT_STD. + */ +void transport_log_set_output_v(const unsigned mask, void *const arg, + const transport_log_output_cb callback); +static _TRANSPORT_LOG_INLINE void transport_log_set_output_p( + const transport_log_output *const output) { + transport_log_set_output_v(output->mask, output->arg, output->callback); +} + +/* Used with _AUX macros and allows to override global format and output + * facility. Use TRANSPORT_LOG_GLOBAL_FORMAT and TRANSPORT_LOG_GLOBAL_OUTPUT for + * values from global configuration. Example: + * + * static const transport_log_output module_output = { + * TRANSPORT_LOG_PUT_STD, 0, custom_output_callback + * }; + * static const transport_log_spec module_spec = { + * TRANSPORT_LOG_GLOBAL_FORMAT, &module_output + * }; + * TRANSPORT_LOGI_AUX(&module_spec, "Position: %ix%i", x, y); + * + * See TRANSPORT_LOGF_AUX and TRANSPORT_LOGF_MEM_AUX for details. + */ +typedef struct transport_log_spec { + const transport_log_format *format; + const transport_log_output *output; +} transport_log_spec; + +#ifdef __cplusplus +} +#endif + +/* Execute log statement if condition is true. Example: + * + * TRANSPORT_LOG_IF(1 < 2, TRANSPORT_LOGI("Log this")); + * TRANSPORT_LOG_IF(1 > 2, TRANSPORT_LOGI("Don't log this")); + * + * Keep in mind though, that if condition can't be evaluated at compile time, + * then it will be evaluated at run time. This will increase exectuable size + * and can have noticeable performance overhead. Try to limit conditions to + * expressions that can be evaluated at compile time. + */ +#define TRANSPORT_LOG_IF(cond, f) \ + do { \ + _TRANSPORT_LOG_IF((cond)) { f; } \ + } \ + _TRANSPORT_LOG_ONCE + +/* Mark log statement as "secret". Log statements that are marked as secrets + * will NOT be executed when censoring is enabled (see TRANSPORT_LOG_CENSORED). + * Example: + * + * TRANSPORT_LOG_SECRET(TRANSPORT_LOGI("Credit card: %s", credit_card)); + * TRANSPORT_LOG_SECRET(TRANSPORT_LOGD_MEM(cipher, cipher_sz, "Cipher + * bytes:")); + */ +#define TRANSPORT_LOG_SECRET(f) TRANSPORT_LOG_IF(TRANSPORT_LOG_SECRETS, f) + +/* Check "current" log level at compile time (ignoring "output" log level). + * Evaluates to true when specified log level is enabled. For example: + * + * #if TRANSPORT_LOG_ENABLED_DEBUG + * const char *const g_enum_strings[] = { + * "enum_value_0", "enum_value_1", "enum_value_2" + * }; + * #endif + * // ... + * #if TRANSPORT_LOG_ENABLED_DEBUG + * TRANSPORT_LOGD("enum value: %s", g_enum_strings[v]); + * #endif + * + * See TRANSPORT_LOG_LEVEL for details. + */ +#define TRANSPORT_LOG_ENABLED(lvl) ((lvl) >= _TRANSPORT_LOG_LEVEL) +#define TRANSPORT_LOG_ENABLED_VERBOSE \ + TRANSPORT_LOG_ENABLED(TRANSPORT_LOG_VERBOSE) +#define TRANSPORT_LOG_ENABLED_DEBUG TRANSPORT_LOG_ENABLED(TRANSPORT_LOG_DEBUG) +#define TRANSPORT_LOG_ENABLED_INFO TRANSPORT_LOG_ENABLED(TRANSPORT_LOG_INFO) +#define TRANSPORT_LOG_ENABLED_WARN TRANSPORT_LOG_ENABLED(TRANSPORT_LOG_WARN) +#define TRANSPORT_LOG_ENABLED_ERROR TRANSPORT_LOG_ENABLED(TRANSPORT_LOG_ERROR) +#define TRANSPORT_LOG_ENABLED_FATAL TRANSPORT_LOG_ENABLED(TRANSPORT_LOG_FATAL) + +/* Check "output" log level at run time (taking into account "current" log + * level as well). Evaluates to true when specified log level is turned on AND + * enabled. For example: + * + * if (TRANSPORT_LOG_ON_DEBUG) + * { + * char hash[65]; + * sha256(data_ptr, data_sz, hash); + * TRANSPORT_LOGD("data: len=%u, sha256=%s", data_sz, hash); + * } + * + * See TRANSPORT_LOG_OUTPUT_LEVEL for details. + */ +#define TRANSPORT_LOG_ON(lvl) \ + (TRANSPORT_LOG_ENABLED((lvl)) && (lvl) >= _TRANSPORT_LOG_OUTPUT_LEVEL) +#define TRANSPORT_LOG_ON_VERBOSE TRANSPORT_LOG_ON(TRANSPORT_LOG_VERBOSE) +#define TRANSPORT_LOG_ON_DEBUG TRANSPORT_LOG_ON(TRANSPORT_LOG_DEBUG) +#define TRANSPORT_LOG_ON_INFO TRANSPORT_LOG_ON(TRANSPORT_LOG_INFO) +#define TRANSPORT_LOG_ON_WARN TRANSPORT_LOG_ON(TRANSPORT_LOG_WARN) +#define TRANSPORT_LOG_ON_ERROR TRANSPORT_LOG_ON(TRANSPORT_LOG_ERROR) +#define TRANSPORT_LOG_ON_FATAL TRANSPORT_LOG_ON(TRANSPORT_LOG_FATAL) + +#ifdef __cplusplus +extern "C" { +#endif + +extern const char *_transport_log_tag_prefix; +extern transport_log_format _transport_log_global_format; +extern transport_log_output _transport_log_global_output; +extern int _transport_log_global_output_lvl; +extern const transport_log_spec _transport_log_stderr_spec; + +void _transport_log_write_d(const char *const func, const char *const file, + const unsigned line, const int lvl, + const char *const tag, const char *const fmt, ...) + _TRANSPORT_LOG_PRINTFLIKE(6, 7); +void _transport_log_write_aux_d(const char *const func, const char *const file, + const unsigned line, + const transport_log_spec *const log, + const int lvl, const char *const tag, + const char *const fmt, ...) + _TRANSPORT_LOG_PRINTFLIKE(7, 8); +void _transport_log_write(const int lvl, const char *const tag, + const char *const fmt, ...) + _TRANSPORT_LOG_PRINTFLIKE(3, 4); +void _transport_log_write_aux(const transport_log_spec *const log, + const int lvl, const char *const tag, + const char *const fmt, ...) + _TRANSPORT_LOG_PRINTFLIKE(4, 5); +void _transport_log_write_mem_d(const char *const func, const char *const file, + const unsigned line, const int lvl, + const char *const tag, const void *const d, + const unsigned d_sz, const char *const fmt, ...) + _TRANSPORT_LOG_PRINTFLIKE(8, 9); +void _transport_log_write_mem_aux_d(const char *const func, + const char *const file, const unsigned line, + const transport_log_spec *const log, + const int lvl, const char *const tag, + const void *const d, const unsigned d_sz, + const char *const fmt, ...) + _TRANSPORT_LOG_PRINTFLIKE(9, 10); +void _transport_log_write_mem(const int lvl, const char *const tag, + const void *const d, const unsigned d_sz, + const char *const fmt, ...) + _TRANSPORT_LOG_PRINTFLIKE(5, 6); +void _transport_log_write_mem_aux(const transport_log_spec *const log, + const int lvl, const char *const tag, + const void *const d, const unsigned d_sz, + const char *const fmt, ...) + _TRANSPORT_LOG_PRINTFLIKE(6, 7); + +#ifdef __cplusplus +} +#endif + +/* Message logging macros: + * - TRANSPORT_LOGV("format string", args, ...) + * - TRANSPORT_LOGD("format string", args, ...) + * - TRANSPORT_LOGI("format string", args, ...) + * - TRANSPORT_LOGW("format string", args, ...) + * - TRANSPORT_LOGE("format string", args, ...) + * - TRANSPORT_LOGF("format string", args, ...) + * + * Memory logging macros: + * - TRANSPORT_LOGV_MEM(data_ptr, data_sz, "format string", args, ...) + * - TRANSPORT_LOGD_MEM(data_ptr, data_sz, "format string", args, ...) + * - TRANSPORT_LOGI_MEM(data_ptr, data_sz, "format string", args, ...) + * - TRANSPORT_LOGW_MEM(data_ptr, data_sz, "format string", args, ...) + * - TRANSPORT_LOGE_MEM(data_ptr, data_sz, "format string", args, ...) + * - TRANSPORT_LOGF_MEM(data_ptr, data_sz, "format string", args, ...) + * + * Auxiliary logging macros: + * - TRANSPORT_LOGV_AUX(&log_instance, "format string", args, ...) + * - TRANSPORT_LOGD_AUX(&log_instance, "format string", args, ...) + * - TRANSPORT_LOGI_AUX(&log_instance, "format string", args, ...) + * - TRANSPORT_LOGW_AUX(&log_instance, "format string", args, ...) + * - TRANSPORT_LOGE_AUX(&log_instance, "format string", args, ...) + * - TRANSPORT_LOGF_AUX(&log_instance, "format string", args, ...) + * + * Auxiliary memory logging macros: + * - TRANSPORT_LOGV_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", + * args, ...) + * - TRANSPORT_LOGD_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", + * args, ...) + * - TRANSPORT_LOGI_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", + * args, ...) + * - TRANSPORT_LOGW_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", + * args, ...) + * - TRANSPORT_LOGE_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", + * args, ...) + * - TRANSPORT_LOGF_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", + * args, ...) + * + * Preformatted string logging macros: + * - TRANSPORT_LOGV_STR("preformatted string"); + * - TRANSPORT_LOGD_STR("preformatted string"); + * - TRANSPORT_LOGI_STR("preformatted string"); + * - TRANSPORT_LOGW_STR("preformatted string"); + * - TRANSPORT_LOGE_STR("preformatted string"); + * - TRANSPORT_LOGF_STR("preformatted string"); + * + * Explicit log level and tag macros: + * - TRANSPORT_LOG_WRITE(level, tag, "format string", args, ...) + * - TRANSPORT_LOG_WRITE_MEM(level, tag, data_ptr, data_sz, "format string", + * args, ...) + * - TRANSPORT_LOG_WRITE_AUX(&log_instance, level, tag, "format string", args, + * ...) + * - TRANSPORT_LOG_WRITE_MEM_AUX(&log_instance, level, tag, data_ptr, data_sz, + * "format string", args, ...) + * + * Format string follows printf() conventions. Both data_ptr and data_sz could + * be 0. Tag can be 0 as well. Most compilers will verify that type of arguments + * match format specifiers in format string. + * + * Library assuming UTF-8 encoding for all strings (char *), including format + * string itself. + */ +#if TRANSPORT_LOG_SRCLOC_NONE == _TRANSPORT_LOG_SRCLOC +#define TRANSPORT_LOG_WRITE(lvl, tag, ...) \ + do { \ + if (TRANSPORT_LOG_ON(lvl)) _transport_log_write(lvl, tag, __VA_ARGS__); \ + } \ + _TRANSPORT_LOG_ONCE +#define TRANSPORT_LOG_WRITE_MEM(lvl, tag, d, d_sz, ...) \ + do { \ + if (TRANSPORT_LOG_ON(lvl)) \ + _transport_log_write_mem(lvl, tag, d, d_sz, __VA_ARGS__); \ + } \ + _TRANSPORT_LOG_ONCE +#define TRANSPORT_LOG_WRITE_AUX(log, lvl, tag, ...) \ + do { \ + if (TRANSPORT_LOG_ON(lvl)) \ + _transport_log_write_aux(log, lvl, tag, __VA_ARGS__); \ + } \ + _TRANSPORT_LOG_ONCE +#define TRANSPORT_LOG_WRITE_MEM_AUX(log, lvl, tag, d, d_sz, ...) \ + do { \ + if (TRANSPORT_LOG_ON(lvl)) \ + _transport_log_write_mem_aux(log, lvl, tag, d, d_sz, __VA_ARGS__); \ + } \ + _TRANSPORT_LOG_ONCE +#else +#define TRANSPORT_LOG_WRITE(lvl, tag, ...) \ + do { \ + if (TRANSPORT_LOG_ON(lvl)) \ + _transport_log_write_d(_TRANSPORT_LOG_SRCLOC_FUNCTION, __FILE__, \ + __LINE__, lvl, tag, __VA_ARGS__); \ + } \ + _TRANSPORT_LOG_ONCE +#define TRANSPORT_LOG_WRITE_MEM(lvl, tag, d, d_sz, ...) \ + do { \ + if (TRANSPORT_LOG_ON(lvl)) \ + _transport_log_write_mem_d(_TRANSPORT_LOG_SRCLOC_FUNCTION, __FILE__, \ + __LINE__, lvl, tag, d, d_sz, __VA_ARGS__); \ + } \ + _TRANSPORT_LOG_ONCE +#define TRANSPORT_LOG_WRITE_AUX(log, lvl, tag, ...) \ + do { \ + if (TRANSPORT_LOG_ON(lvl)) \ + _transport_log_write_aux_d(_TRANSPORT_LOG_SRCLOC_FUNCTION, __FILE__, \ + __LINE__, log, lvl, tag, __VA_ARGS__); \ + } \ + _TRANSPORT_LOG_ONCE +#define TRANSPORT_LOG_WRITE_MEM_AUX(log, lvl, tag, d, d_sz, ...) \ + do { \ + if (TRANSPORT_LOG_ON(lvl)) \ + _transport_log_write_mem_aux_d(_TRANSPORT_LOG_SRCLOC_FUNCTION, __FILE__, \ + __LINE__, log, lvl, tag, d, d_sz, \ + __VA_ARGS__); \ + } \ + _TRANSPORT_LOG_ONCE +#endif + +static _TRANSPORT_LOG_INLINE void _transport_log_unused(const int dummy, ...) { + (void)dummy; +} + +#define _TRANSPORT_LOG_UNUSED(...) \ + do { \ + _TRANSPORT_LOG_NEVER _transport_log_unused(0, __VA_ARGS__); \ + } \ + _TRANSPORT_LOG_ONCE + +#if TRANSPORT_LOG_ENABLED_VERBOSE +#define TRANSPORT_LOGV(...) \ + TRANSPORT_LOG_WRITE(TRANSPORT_LOG_VERBOSE, _TRANSPORT_LOG_TAG, __VA_ARGS__) +#define TRANSPORT_LOGV_AUX(log, ...) \ + TRANSPORT_LOG_WRITE_AUX(log, TRANSPORT_LOG_VERBOSE, _TRANSPORT_LOG_TAG, \ + __VA_ARGS__) +#define TRANSPORT_LOGV_MEM(d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM(TRANSPORT_LOG_VERBOSE, _TRANSPORT_LOG_TAG, d, d_sz, \ + __VA_ARGS__) +#define TRANSPORT_LOGV_MEM_AUX(log, d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM(log, TRANSPORT_LOG_VERBOSE, _TRANSPORT_LOG_TAG, d, \ + d_sz, __VA_ARGS__) +#else +#define TRANSPORT_LOGV(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGV_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGV_MEM(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGV_MEM_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#endif + +#if TRANSPORT_LOG_ENABLED_DEBUG +#define TRANSPORT_LOGD(...) \ + TRANSPORT_LOG_WRITE(TRANSPORT_LOG_DEBUG, _TRANSPORT_LOG_TAG, __VA_ARGS__) +#define TRANSPORT_LOGD_AUX(log, ...) \ + TRANSPORT_LOG_WRITE_AUX(log, TRANSPORT_LOG_DEBUG, _TRANSPORT_LOG_TAG, \ + __VA_ARGS__) +#define TRANSPORT_LOGD_MEM(d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM(TRANSPORT_LOG_DEBUG, _TRANSPORT_LOG_TAG, d, d_sz, \ + __VA_ARGS__) +#define TRANSPORT_LOGD_MEM_AUX(log, d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM_AUX(log, TRANSPORT_LOG_DEBUG, _TRANSPORT_LOG_TAG, d, \ + d_sz, __VA_ARGS__) +#else +#define TRANSPORT_LOGD(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGD_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGD_MEM(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGD_MEM_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#endif + +#if TRANSPORT_LOG_ENABLED_INFO +#define TRANSPORT_LOGI(...) \ + TRANSPORT_LOG_WRITE(TRANSPORT_LOG_INFO, _TRANSPORT_LOG_TAG, __VA_ARGS__) +#define TRANSPORT_LOGI_AUX(log, ...) \ + TRANSPORT_LOG_WRITE_AUX(log, TRANSPORT_LOG_INFO, _TRANSPORT_LOG_TAG, \ + __VA_ARGS__) +#define TRANSPORT_LOGI_MEM(d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM(TRANSPORT_LOG_INFO, _TRANSPORT_LOG_TAG, d, d_sz, \ + __VA_ARGS__) +#define TRANSPORT_LOGI_MEM_AUX(log, d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM_AUX(log, TRANSPORT_LOG_INFO, _TRANSPORT_LOG_TAG, d, \ + d_sz, __VA_ARGS__) +#else +#define TRANSPORT_LOGI(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGI_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGI_MEM(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGI_MEM_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#endif + +#if TRANSPORT_LOG_ENABLED_WARN +#define TRANSPORT_LOGW(...) \ + TRANSPORT_LOG_WRITE(TRANSPORT_LOG_WARN, _TRANSPORT_LOG_TAG, __VA_ARGS__) +#define TRANSPORT_LOGW_AUX(log, ...) \ + TRANSPORT_LOG_WRITE_AUX(log, TRANSPORT_LOG_WARN, _TRANSPORT_LOG_TAG, \ + __VA_ARGS__) +#define TRANSPORT_LOGW_MEM(d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM(TRANSPORT_LOG_WARN, _TRANSPORT_LOG_TAG, d, d_sz, \ + __VA_ARGS__) +#define TRANSPORT_LOGW_MEM_AUX(log, d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM_AUX(log, TRANSPORT_LOG_WARN, _TRANSPORT_LOG_TAG, d, \ + d_sz, __VA_ARGS__) +#else +#define TRANSPORT_LOGW(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGW_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGW_MEM(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGW_MEM_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#endif + +#if TRANSPORT_LOG_ENABLED_ERROR +#define TRANSPORT_LOGE(...) \ + TRANSPORT_LOG_WRITE(TRANSPORT_LOG_ERROR, _TRANSPORT_LOG_TAG, __VA_ARGS__) +#define TRANSPORT_LOGE_AUX(log, ...) \ + TRANSPORT_LOG_WRITE_AUX(log, TRANSPORT_LOG_ERROR, _TRANSPORT_LOG_TAG, \ + __VA_ARGS__) +#define TRANSPORT_LOGE_MEM(d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM(TRANSPORT_LOG_ERROR, _TRANSPORT_LOG_TAG, d, d_sz, \ + __VA_ARGS__) +#define TRANSPORT_LOGE_MEM_AUX(log, d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM_AUX(log, TRANSPORT_LOG_ERROR, _TRANSPORT_LOG_TAG, d, \ + d_sz, __VA_ARGS__) +#else +#define TRANSPORT_LOGE(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGE_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGE_MEM(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGE_MEM_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#endif + +#if TRANSPORT_LOG_ENABLED_FATAL +#define TRANSPORT_LOGF(...) \ + TRANSPORT_LOG_WRITE(TRANSPORT_LOG_FATAL, _TRANSPORT_LOG_TAG, __VA_ARGS__) +#define TRANSPORT_LOGF_AUX(log, ...) \ + TRANSPORT_LOG_WRITE_AUX(log, TRANSPORT_LOG_FATAL, _TRANSPORT_LOG_TAG, \ + __VA_ARGS__) +#define TRANSPORT_LOGF_MEM(d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM(TRANSPORT_LOG_FATAL, _TRANSPORT_LOG_TAG, d, d_sz, \ + __VA_ARGS__) +#define TRANSPORT_LOGF_MEM_AUX(log, d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM_AUX(log, TRANSPORT_LOG_FATAL, _TRANSPORT_LOG_TAG, d, \ + d_sz, __VA_ARGS__) +#else +#define TRANSPORT_LOGF(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGF_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGF_MEM(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGF_MEM_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#endif + +#define TRANSPORT_LOGV_STR(s) TRANSPORT_LOGV("%s", (s)) +#define TRANSPORT_LOGD_STR(s) TRANSPORT_LOGD("%s", (s)) +#define TRANSPORT_LOGI_STR(s) TRANSPORT_LOGI("%s", (s)) +#define TRANSPORT_LOGW_STR(s) TRANSPORT_LOGW("%s", (s)) +#define TRANSPORT_LOGE_STR(s) TRANSPORT_LOGE("%s", (s)) +#define TRANSPORT_LOGF_STR(s) TRANSPORT_LOGF("%s", (s)) + +#ifdef __cplusplus +extern "C" { +#endif + +/* Output to standard error stream. Library uses it by default, though in few + * cases it could be necessary to specify it explicitly. For example, when + * transport_log library is compiled with TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT, + * application must define and initialize global output variable: + * + * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {TRANSPORT_LOG_OUT_STDERR}; + * + * Another example is when using custom output, stderr could be used as a + * fallback when custom output facility failed to initialize: + * + * transport_log_set_output_v(TRANSPORT_LOG_OUT_STDERR); + */ +enum { TRANSPORT_LOG_OUT_STDERR_MASK = TRANSPORT_LOG_PUT_STD }; +void transport_log_out_stderr_callback(const transport_log_message *const msg, + void *arg); +#define TRANSPORT_LOG_OUT_STDERR \ + TRANSPORT_LOG_OUT_STDERR_MASK, 0, transport_log_out_stderr_callback + +/* Predefined spec for stderr. Uses global format options + * (TRANSPORT_LOG_GLOBAL_FORMAT) and TRANSPORT_LOG_OUT_STDERR. Could be used to + * force output to stderr for a particular message. Example: + * + * f = fopen("foo.log", "w"); + * if (!f) + * TRANSPORT_LOGE_AUX(TRANSPORT_LOG_STDERR, "Failed to open log file"); + */ +#define TRANSPORT_LOG_STDERR (&_transport_log_stderr_spec) + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/membuf.cc b/libtransport/src/hicn/transport/utils/membuf.cc new file mode 100755 index 000000000..0ab1a6044 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/membuf.cc @@ -0,0 +1,864 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright 2013-present Facebook, Inc. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * The code in this file if adapated from the IOBuf of folly: + * https://github.com/facebook/folly/blob/master/folly/io/IOBuf.h + */ + +#include + +#include +#include +#include +#include +#include +#include + +using std::unique_ptr; + +namespace { + +enum : uint16_t { + kHeapMagic = 0xa5a5, + // This memory segment contains an MemBuf that is still in use + kMemBufInUse = 0x01, + // This memory segment contains buffer data that is still in use + kDataInUse = 0x02, +}; + +enum : std::size_t { + // When create() is called for buffers less than kDefaultCombinedBufSize, + // we allocate a single combined memory segment for the MemBuf and the data + // together. See the comments for createCombined()/createSeparate() for more + // details. + // + // (The size of 1k is largely just a guess here. We could could probably do + // benchmarks of real applications to see if adjusting this number makes a + // difference. Callers that know their exact use case can also explicitly + // call createCombined() or createSeparate().) + kDefaultCombinedBufSize = 1024 +}; + +// Helper function for MemBuf::takeOwnership() +void takeOwnershipError(bool freeOnError, void* buf, + utils::MemBuf::FreeFunction freeFn, void* userData) { + if (!freeOnError) { + return; + } + if (!freeFn) { + free(buf); + return; + } + try { + freeFn(buf, userData); + } catch (...) { + // The user's free function is not allowed to throw. + // (We are already in the middle of throwing an exception, so + // we cannot let this exception go unhandled.) + abort(); + } +} + +} // namespace + +namespace utils { + +struct MemBuf::HeapPrefix { + explicit HeapPrefix(uint16_t flg) : magic(kHeapMagic), flags(flg) {} + ~HeapPrefix() { + // Reset magic to 0 on destruction. This is solely for debugging purposes + // to help catch bugs where someone tries to use HeapStorage after it has + // been deleted. + magic = 0; + } + + uint16_t magic; + std::atomic flags; +}; + +struct MemBuf::HeapStorage { + HeapPrefix prefix; + // The MemBuf is last in the HeapStorage object. + // This way operator new will work even if allocating a subclass of MemBuf + // that requires more space. + utils::MemBuf buf; +}; + +struct MemBuf::HeapFullStorage { + // Make sure jemalloc allocates from the 64-byte class. Putting this here + // because HeapStorage is private so it can't be at namespace level. + static_assert(sizeof(HeapStorage) <= 64, + "MemBuf may not grow over 56 bytes!"); + + HeapStorage hs; + SharedInfo shared; + std::max_align_t align; +}; + +MemBuf::SharedInfo::SharedInfo() : freeFn(nullptr), userData(nullptr) { + // Use relaxed memory ordering here. Since we are creating a new SharedInfo, + // no other threads should be referring to it yet. + refcount.store(1, std::memory_order_relaxed); +} + +MemBuf::SharedInfo::SharedInfo(FreeFunction fn, void* arg) + : freeFn(fn), userData(arg) { + // Use relaxed memory ordering here. Since we are creating a new SharedInfo, + // no other threads should be referring to it yet. + refcount.store(1, std::memory_order_relaxed); +} + +void* MemBuf::operator new(size_t size) { + size_t fullSize = offsetof(HeapStorage, buf) + size; + auto* storage = static_cast(malloc(fullSize)); + + new (&storage->prefix) HeapPrefix(kMemBufInUse); + return &(storage->buf); +} + +void* MemBuf::operator new(size_t /* size */, void* ptr) { return ptr; } + +void MemBuf::operator delete(void* ptr) { + auto* storageAddr = static_cast(ptr) - offsetof(HeapStorage, buf); + auto* storage = reinterpret_cast(storageAddr); + releaseStorage(storage, kMemBufInUse); +} + +void MemBuf::operator delete(void* /* ptr */, void* /* placement */) { + // Provide matching operator for `MemBuf::new` to avoid MSVC compilation + // warning (C4291) about memory leak when exception is thrown in the + // constructor. +} + +void MemBuf::releaseStorage(HeapStorage* storage, uint16_t freeFlags) { + // Use relaxed memory order here. If we are unlucky and happen to get + // out-of-date data the compare_exchange_weak() call below will catch + // it and load new data with memory_order_acq_rel. + auto flags = storage->prefix.flags.load(std::memory_order_acquire); + + while (true) { + uint16_t newFlags = uint16_t(flags & ~freeFlags); + if (newFlags == 0) { + // The storage space is now unused. Free it. + storage->prefix.HeapPrefix::~HeapPrefix(); + free(storage); + return; + } + + // This storage segment still contains portions that are in use. + // Just clear the flags specified in freeFlags for now. + auto ret = storage->prefix.flags.compare_exchange_weak( + flags, newFlags, std::memory_order_acq_rel); + if (ret) { + // We successfully updated the flags. + return; + } + + // We failed to update the flags. Some other thread probably updated them + // and cleared some of the other bits. Continue around the loop to see if + // we are the last user now, or if we need to try updating the flags again. + } +} + +void MemBuf::freeInternalBuf(void* /* buf */, void* userData) { + auto* storage = static_cast(userData); + releaseStorage(storage, kDataInUse); +} + +MemBuf::MemBuf(CreateOp, std::size_t capacity) + : next_(this), + prev_(this), + data_(nullptr), + length_(0), + flags_and_shared_info_(0) { + SharedInfo* info; + allocExtBuffer(capacity, &buf_, &info, &capacity_); + setSharedInfo(info); + data_ = buf_; +} + +MemBuf::MemBuf(CopyBufferOp /* op */, const void* buf, std::size_t size, + std::size_t headroom, std::size_t min_tailroom) + : MemBuf(CREATE, headroom + size + min_tailroom) { + advance(headroom); + if (size > 0) { + assert(buf != nullptr); + memcpy(writableData(), buf, size); + append(size); + } +} + +unique_ptr MemBuf::create(std::size_t capacity) { + // For smaller-sized buffers, allocate the MemBuf, SharedInfo, and the buffer + // all with a single allocation. + // + // We don't do this for larger buffers since it can be wasteful if the user + // needs to reallocate the buffer but keeps using the same MemBuf object. + // In this case we can't free the data space until the MemBuf is also + // destroyed. Callers can explicitly call createCombined() or + // createSeparate() if they know their use case better, and know if they are + // likely to reallocate the buffer later. + if (capacity <= kDefaultCombinedBufSize) { + return createCombined(capacity); + } + return createSeparate(capacity); +} + +unique_ptr MemBuf::createCombined(std::size_t capacity) { + // To save a memory allocation, allocate space for the MemBuf object, the + // SharedInfo struct, and the data itself all with a single call to malloc(). + size_t requiredStorage = offsetof(HeapFullStorage, align) + capacity; + size_t mallocSize = requiredStorage; + auto* storage = static_cast(malloc(mallocSize)); + + new (&storage->hs.prefix) HeapPrefix(kMemBufInUse | kDataInUse); + new (&storage->shared) SharedInfo(freeInternalBuf, storage); + + uint8_t* bufAddr = reinterpret_cast(&storage->align); + uint8_t* storageEnd = reinterpret_cast(storage) + mallocSize; + size_t actualCapacity = size_t(storageEnd - bufAddr); + unique_ptr ret(new (&storage->hs.buf) MemBuf( + InternalConstructor(), packFlagsAndSharedInfo(0, &storage->shared), + bufAddr, actualCapacity, bufAddr, 0)); + return ret; +} + +unique_ptr MemBuf::createSeparate(std::size_t capacity) { + return std::make_unique(CREATE, capacity); +} + +unique_ptr MemBuf::createChain(size_t totalCapacity, + std::size_t maxBufCapacity) { + unique_ptr out = + create(std::min(totalCapacity, size_t(maxBufCapacity))); + size_t allocatedCapacity = out->capacity(); + + while (allocatedCapacity < totalCapacity) { + unique_ptr newBuf = create( + std::min(totalCapacity - allocatedCapacity, size_t(maxBufCapacity))); + allocatedCapacity += newBuf->capacity(); + out->prependChain(std::move(newBuf)); + } + + return out; +} + +MemBuf::MemBuf(TakeOwnershipOp, void* buf, std::size_t capacity, + std::size_t length, FreeFunction freeFn, void* userData, + bool freeOnError) + : next_(this), + prev_(this), + data_(static_cast(buf)), + buf_(static_cast(buf)), + length_(length), + capacity_(capacity), + flags_and_shared_info_( + packFlagsAndSharedInfo(flag_free_shared_info, nullptr)) { + try { + setSharedInfo(new SharedInfo(freeFn, userData)); + } catch (...) { + takeOwnershipError(freeOnError, buf, freeFn, userData); + throw; + } +} + +unique_ptr MemBuf::takeOwnership(void* buf, std::size_t capacity, + std::size_t length, + FreeFunction freeFn, void* userData, + bool freeOnError) { + try { + // TODO: We could allocate the MemBuf object and SharedInfo all in a single + // memory allocation. We could use the existing HeapStorage class, and + // define a new kSharedInfoInUse flag. We could change our code to call + // releaseStorage(flag_free_shared_info) when this flag_free_shared_info, + // rather than directly calling delete. + // + // Note that we always pass freeOnError as false to the constructor. + // If the constructor throws we'll handle it below. (We have to handle + // allocation failures from std::make_unique too.) + return std::make_unique(TAKE_OWNERSHIP, buf, capacity, length, + freeFn, userData, false); + } catch (...) { + takeOwnershipError(freeOnError, buf, freeFn, userData); + throw; + } +} + +MemBuf::MemBuf(WrapBufferOp, const void* buf, std::size_t capacity) noexcept + : MemBuf(InternalConstructor(), 0, + // We cast away the const-ness of the buffer here. + // This is okay since MemBuf users must use unshare() to create a + // copy of this buffer before writing to the buffer. + static_cast(const_cast(buf)), capacity, + static_cast(const_cast(buf)), capacity) {} + +unique_ptr MemBuf::wrapBuffer(const void* buf, std::size_t capacity) { + return std::make_unique(WRAP_BUFFER, buf, capacity); +} + +MemBuf MemBuf::wrapBufferAsValue(const void* buf, + std::size_t capacity) noexcept { + return MemBuf(WrapBufferOp::WRAP_BUFFER, buf, capacity); +} + +MemBuf::MemBuf() noexcept {} + +MemBuf::MemBuf(MemBuf&& other) noexcept + : data_(other.data_), + buf_(other.buf_), + length_(other.length_), + capacity_(other.capacity_), + flags_and_shared_info_(other.flags_and_shared_info_) { + // Reset other so it is a clean state to be destroyed. + other.data_ = nullptr; + other.buf_ = nullptr; + other.length_ = 0; + other.capacity_ = 0; + other.flags_and_shared_info_ = 0; + + // If other was part of the chain, assume ownership of the rest of its chain. + // (It's only valid to perform move assignment on the head of a chain.) + if (other.next_ != &other) { + next_ = other.next_; + next_->prev_ = this; + other.next_ = &other; + + prev_ = other.prev_; + prev_->next_ = this; + other.prev_ = &other; + } +} + +MemBuf::MemBuf(const MemBuf& other) { *this = other.cloneAsValue(); } + +MemBuf::MemBuf(InternalConstructor, uintptr_t flagsAndSharedInfo, uint8_t* buf, + std::size_t capacity, uint8_t* data, std::size_t length) noexcept + : next_(this), + prev_(this), + data_(data), + buf_(buf), + length_(length), + capacity_(capacity), + flags_and_shared_info_(flagsAndSharedInfo) { + assert(data >= buf); + assert(data + length <= buf + capacity); +} + +MemBuf::~MemBuf() { + // Destroying an MemBuf destroys the entire chain. + // Users of MemBuf should only explicitly delete the head of any chain. + // The other elements in the chain will be automatically destroyed. + while (next_ != this) { + // Since unlink() returns unique_ptr() and we don't store it, + // it will automatically delete the unlinked element. + (void)next_->unlink(); + } + + decrementRefcount(); +} + +MemBuf& MemBuf::operator=(MemBuf&& other) noexcept { + if (this == &other) { + return *this; + } + + // If we are part of a chain, delete the rest of the chain. + while (next_ != this) { + // Since unlink() returns unique_ptr() and we don't store it, + // it will automatically delete the unlinked element. + (void)next_->unlink(); + } + + // Decrement our refcount on the current buffer + decrementRefcount(); + + // Take ownership of the other buffer's data + data_ = other.data_; + buf_ = other.buf_; + length_ = other.length_; + capacity_ = other.capacity_; + flags_and_shared_info_ = other.flags_and_shared_info_; + // Reset other so it is a clean state to be destroyed. + other.data_ = nullptr; + other.buf_ = nullptr; + other.length_ = 0; + other.capacity_ = 0; + other.flags_and_shared_info_ = 0; + + // If other was part of the chain, assume ownership of the rest of its chain. + // (It's only valid to perform move assignment on the head of a chain.) + if (other.next_ != &other) { + next_ = other.next_; + next_->prev_ = this; + other.next_ = &other; + + prev_ = other.prev_; + prev_->next_ = this; + other.prev_ = &other; + } + + return *this; +} + +MemBuf& MemBuf::operator=(const MemBuf& other) { + if (this != &other) { + *this = MemBuf(other); + } + return *this; +} + +bool MemBuf::empty() const { + const MemBuf* current = this; + do { + if (current->length() != 0) { + return false; + } + current = current->next_; + } while (current != this); + return true; +} + +size_t MemBuf::countChainElements() const { + size_t numElements = 1; + for (MemBuf* current = next_; current != this; current = current->next_) { + ++numElements; + } + return numElements; +} + +std::size_t MemBuf::computeChainDataLength() const { + std::size_t fullLength = length_; + for (MemBuf* current = next_; current != this; current = current->next_) { + fullLength += current->length_; + } + return fullLength; +} + +void MemBuf::prependChain(unique_ptr&& iobuf) { + // Take ownership of the specified MemBuf + MemBuf* other = iobuf.release(); + + // Remember the pointer to the tail of the other chain + MemBuf* otherTail = other->prev_; + + // Hook up prev_->next_ to point at the start of the other chain, + // and other->prev_ to point at prev_ + prev_->next_ = other; + other->prev_ = prev_; + + // Hook up otherTail->next_ to point at us, + // and prev_ to point back at otherTail, + otherTail->next_ = this; + prev_ = otherTail; +} + +unique_ptr MemBuf::clone() const { + return std::make_unique(cloneAsValue()); +} + +unique_ptr MemBuf::cloneOne() const { + return std::make_unique(cloneOneAsValue()); +} + +unique_ptr MemBuf::cloneCoalesced() const { + return std::make_unique(cloneCoalescedAsValue()); +} + +unique_ptr MemBuf::cloneCoalescedWithHeadroomTailroom( + std::size_t new_headroom, std::size_t new_tailroom) const { + return std::make_unique( + cloneCoalescedAsValueWithHeadroomTailroom(new_headroom, new_tailroom)); +} + +MemBuf MemBuf::cloneAsValue() const { + auto tmp = cloneOneAsValue(); + + for (MemBuf* current = next_; current != this; current = current->next_) { + tmp.prependChain(current->cloneOne()); + } + + return tmp; +} + +MemBuf MemBuf::cloneOneAsValue() const { + if (SharedInfo* info = sharedInfo()) { + setFlags(flag_maybe_shared); + info->refcount.fetch_add(1, std::memory_order_acq_rel); + } + return MemBuf(InternalConstructor(), flags_and_shared_info_, buf_, capacity_, + data_, length_); +} + +MemBuf MemBuf::cloneCoalescedAsValue() const { + const std::size_t new_headroom = headroom(); + const std::size_t new_tailroom = prev()->tailroom(); + return cloneCoalescedAsValueWithHeadroomTailroom(new_headroom, new_tailroom); +} + +MemBuf MemBuf::cloneCoalescedAsValueWithHeadroomTailroom( + std::size_t new_headroom, std::size_t new_tailroom) const { + if (!isChained()) { + return cloneOneAsValue(); + } + // Coalesce into newBuf + const std::size_t new_length = computeChainDataLength(); + const std::size_t new_capacity = new_length + new_headroom + new_tailroom; + MemBuf newBuf{CREATE, new_capacity}; + newBuf.advance(new_headroom); + + auto current = this; + do { + if (current->length() > 0) { + memcpy(newBuf.writableTail(), current->data(), current->length()); + newBuf.append(current->length()); + } + current = current->next(); + } while (current != this); + + return newBuf; +} + +void MemBuf::unshareOneSlow() { + // Allocate a new buffer for the data + uint8_t* buf; + SharedInfo* sharedInfo; + std::size_t actualCapacity; + allocExtBuffer(capacity_, &buf, &sharedInfo, &actualCapacity); + + // Copy the data + // Maintain the same amount of headroom. Since we maintained the same + // minimum capacity we also maintain at least the same amount of tailroom. + std::size_t headlen = headroom(); + if (length_ > 0) { + assert(data_ != nullptr); + memcpy(buf + headlen, data_, length_); + } + + // Release our reference on the old buffer + decrementRefcount(); + // Make sure flag_maybe_shared and flag_free_shared_info are all cleared. + setFlagsAndSharedInfo(0, sharedInfo); + + // Update the buffer pointers to point to the new buffer + data_ = buf + headlen; + buf_ = buf; +} + +void MemBuf::unshareChained() { + // unshareChained() should only be called if we are part of a chain of + // multiple MemBufs. The caller should have already verified this. + assert(isChained()); + + MemBuf* current = this; + while (true) { + if (current->isSharedOne()) { + // we have to unshare + break; + } + + current = current->next_; + if (current == this) { + // None of the MemBufs in the chain are shared, + // so return without doing anything + return; + } + } + + // We have to unshare. Let coalesceSlow() do the work. + coalesceSlow(); +} + +void MemBuf::markExternallyShared() { + MemBuf* current = this; + do { + current->markExternallySharedOne(); + current = current->next_; + } while (current != this); +} + +void MemBuf::makeManagedChained() { + assert(isChained()); + + MemBuf* current = this; + while (true) { + current->makeManagedOne(); + current = current->next_; + if (current == this) { + break; + } + } +} + +void MemBuf::coalesceSlow() { + // coalesceSlow() should only be called if we are part of a chain of multiple + // MemBufs. The caller should have already verified this. + + // Compute the length of the entire chain + std::size_t new_length = 0; + MemBuf* end = this; + do { + new_length += end->length_; + end = end->next_; + } while (end != this); + + coalesceAndReallocate(new_length, end); + // We should be only element left in the chain now +} + +void MemBuf::coalesceSlow(size_t max_length) { + // coalesceSlow() should only be called if we are part of a chain of multiple + // MemBufs. The caller should have already verified this. + + // Compute the length of the entire chain + std::size_t new_length = 0; + MemBuf* end = this; + while (true) { + new_length += end->length_; + end = end->next_; + if (new_length >= max_length) { + break; + } + if (end == this) { + throw std::overflow_error( + "attempted to coalesce more data than " + "available"); + } + } + + coalesceAndReallocate(new_length, end); + // We should have the requested length now +} + +void MemBuf::coalesceAndReallocate(size_t new_headroom, size_t new_length, + MemBuf* end, size_t new_tailroom) { + std::size_t new_capacity = new_length + new_headroom + new_tailroom; + + // Allocate space for the coalesced buffer. + // We always convert to an external buffer, even if we happened to be an + // internal buffer before. + uint8_t* newBuf; + SharedInfo* newInfo; + std::size_t actualCapacity; + allocExtBuffer(new_capacity, &newBuf, &newInfo, &actualCapacity); + + // Copy the data into the new buffer + uint8_t* new_data = newBuf + new_headroom; + uint8_t* p = new_data; + MemBuf* current = this; + size_t remaining = new_length; + do { + if (current->length_ > 0) { + assert(current->length_ <= remaining); + assert(current->data_ != nullptr); + remaining -= current->length_; + memcpy(p, current->data_, current->length_); + p += current->length_; + } + current = current->next_; + } while (current != end); + assert(remaining == 0); + + // Point at the new buffer + decrementRefcount(); + + // Make sure flag_maybe_shared and flag_free_shared_info are all cleared. + setFlagsAndSharedInfo(0, newInfo); + + capacity_ = actualCapacity; + buf_ = newBuf; + data_ = new_data; + length_ = new_length; + + // Separate from the rest of our chain. + // Since we don't store the unique_ptr returned by separateChain(), + // this will immediately delete the returned subchain. + if (isChained()) { + (void)separateChain(next_, current->prev_); + } +} + +void MemBuf::decrementRefcount() { + // Externally owned buffers don't have a SharedInfo object and aren't managed + // by the reference count + SharedInfo* info = sharedInfo(); + if (!info) { + return; + } + + // Decrement the refcount + uint32_t newcnt = info->refcount.fetch_sub(1, std::memory_order_acq_rel); + // Note that fetch_sub() returns the value before we decremented. + // If it is 1, we were the only remaining user; if it is greater there are + // still other users. + if (newcnt > 1) { + return; + } + + // We were the last user. Free the buffer + freeExtBuffer(); + + // Free the SharedInfo if it was allocated separately. + // + // This is only used by takeOwnership(). + // + // To avoid this special case handling in decrementRefcount(), we could have + // takeOwnership() set a custom freeFn() that calls the user's free function + // then frees the SharedInfo object. (This would require that + // takeOwnership() store the user's free function with its allocated + // SharedInfo object.) However, handling this specially with a flag seems + // like it shouldn't be problematic. + if (flags() & flag_free_shared_info) { + delete sharedInfo(); + } +} + +void MemBuf::reserveSlow(std::size_t min_headroom, std::size_t min_tailroom) { + size_t new_capacity = (size_t)length_ + min_headroom + min_tailroom; + + // // reserveSlow() is dangerous if anyone else is sharing the buffer, as we + // may + // // reallocate and free the original buffer. It should only ever be called + // if + // // we are the only user of the buffer. + + // We'll need to reallocate the buffer. + // There are a few options. + // - If we have enough total room, move the data around in the buffer + // and adjust the data_ pointer. + // - If we're using an internal buffer, we'll switch to an external + // buffer with enough headroom and tailroom. + // - If we have enough headroom (headroom() >= min_headroom) but not too much + // (so we don't waste memory), we can try: + // - If we don't have too much to copy, we'll use realloc() (note that + // realloc might have to copy + // headroom + data + tailroom) + // - Otherwise, bite the bullet and reallocate. + if (headroom() + tailroom() >= min_headroom + min_tailroom) { + uint8_t* new_data = writableBuffer() + min_headroom; + std::memmove(new_data, data_, length_); + data_ = new_data; + return; + } + + size_t new_allocated_capacity = 0; + uint8_t* new_buffer = nullptr; + std::size_t new_headroom = 0; + std::size_t old_headroom = headroom(); + + // If we have a buffer allocated with malloc and we just need more tailroom, + // try to use realloc()/xallocx() to grow the buffer in place. + SharedInfo* info = sharedInfo(); + if (info && (info->freeFn == nullptr) && length_ != 0 && + old_headroom >= min_headroom) { + size_t head_slack = old_headroom - min_headroom; + new_allocated_capacity = goodExtBufferSize(new_capacity + head_slack); + + size_t copySlack = capacity() - length_; + if (copySlack * 2 <= length_) { + void* p = realloc(buf_, new_allocated_capacity); + if (TRANSPORT_EXPECT_FALSE(p == nullptr)) { + throw std::bad_alloc(); + } + new_buffer = static_cast(p); + new_headroom = old_headroom; + } + } + + // None of the previous reallocation strategies worked (or we're using + // an internal buffer). malloc/copy/free. + if (new_buffer == nullptr) { + new_allocated_capacity = goodExtBufferSize(new_capacity); + new_buffer = static_cast(malloc(new_allocated_capacity)); + if (length_ > 0) { + assert(data_ != nullptr); + memcpy(new_buffer + min_headroom, data_, length_); + } + if (sharedInfo()) { + freeExtBuffer(); + } + new_headroom = min_headroom; + } + + std::size_t cap; + initExtBuffer(new_buffer, new_allocated_capacity, &info, &cap); + + if (flags() & flag_free_shared_info) { + delete sharedInfo(); + } + + setFlagsAndSharedInfo(0, info); + capacity_ = cap; + buf_ = new_buffer; + data_ = new_buffer + new_headroom; + // length_ is unchanged +} + +void MemBuf::freeExtBuffer() { + SharedInfo* info = sharedInfo(); + + if (info->freeFn) { + try { + info->freeFn(buf_, info->userData); + } catch (...) { + // The user's free function should never throw. Otherwise we might + // throw from the MemBuf destructor. Other code paths like coalesce() + // also assume that decrementRefcount() cannot throw. + abort(); + } + } else { + free(buf_); + } +} + +void MemBuf::allocExtBuffer(std::size_t minCapacity, uint8_t** bufReturn, + SharedInfo** infoReturn, + std::size_t* capacityReturn) { + size_t mallocSize = goodExtBufferSize(minCapacity); + uint8_t* buf = static_cast(malloc(mallocSize)); + initExtBuffer(buf, mallocSize, infoReturn, capacityReturn); + *bufReturn = buf; +} + +size_t MemBuf::goodExtBufferSize(std::size_t minCapacity) { + // Determine how much space we should allocate. We'll store the SharedInfo + // for the external buffer just after the buffer itself. (We store it just + // after the buffer rather than just before so that the code can still just + // use free(buf_) to free the buffer.) + size_t minSize = static_cast(minCapacity) + sizeof(SharedInfo); + // Add room for padding so that the SharedInfo will be aligned on an 8-byte + // boundary. + minSize = (minSize + 7) & ~7; + + // Use goodMallocSize() to bump up the capacity to a decent size to request + // from malloc, so we can use all of the space that malloc will probably give + // us anyway. + return minSize; +} + +void MemBuf::initExtBuffer(uint8_t* buf, size_t mallocSize, + SharedInfo** infoReturn, + std::size_t* capacityReturn) { + // Find the SharedInfo storage at the end of the buffer + // and construct the SharedInfo. + uint8_t* infoStart = (buf + mallocSize) - sizeof(SharedInfo); + SharedInfo* sharedInfo = new (infoStart) SharedInfo; + + *capacityReturn = std::size_t(infoStart - buf); + *infoReturn = sharedInfo; +} + +} // namespace utils \ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/membuf.h b/libtransport/src/hicn/transport/utils/membuf.h new file mode 100755 index 000000000..944237e2b --- /dev/null +++ b/libtransport/src/hicn/transport/utils/membuf.h @@ -0,0 +1,916 @@ +/* + * Copyright 2013-present Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * The code in this file if adapated from the IOBuf of folly: + * https://github.com/facebook/folly/blob/master/folly/io/IOBuf.h + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Ignore shadowing warnings within this file, so includers can use -Wshadow. +TRANSPORT_GNU_DISABLE_WARNING("-Wshadow") + +namespace utils { + +class MemBuf { + public: + enum CreateOp { CREATE }; + enum WrapBufferOp { WRAP_BUFFER }; + enum TakeOwnershipOp { TAKE_OWNERSHIP }; + enum CopyBufferOp { COPY_BUFFER }; + + typedef void (*FreeFunction)(void* buf, void* userData); + + static std::unique_ptr create(std::size_t capacity); + MemBuf(CreateOp, std::size_t capacity); + + /** + * Create a new MemBuf, using a single memory allocation to allocate space + * for both the MemBuf object and the data storage space. + * + * This saves one memory allocation. However, it can be wasteful if you + * later need to grow the buffer using reserve(). If the buffer needs to be + * reallocated, the space originally allocated will not be freed() until the + * MemBuf object itself is also freed. (It can also be slightly wasteful in + * some cases where you clone this MemBuf and then free the original MemBuf.) + */ + static std::unique_ptr createCombined(std::size_t capacity); + + /** + * Create a new IOBuf, using separate memory allocations for the IOBuf object + * for the IOBuf and the data storage space. + * + * This requires two memory allocations, but saves space in the long run + * if you know that you will need to reallocate the data buffer later. + */ + static std::unique_ptr createSeparate(std::size_t capacity); + + /** + * Allocate a new MemBuf chain with the requested total capacity, allocating + * no more than maxBufCapacity to each buffer. + */ + static std::unique_ptr createChain(size_t totalCapacity, + std::size_t maxBufCapacity); + + static std::unique_ptr takeOwnership(void* buf, std::size_t capacity, + FreeFunction freeFn = nullptr, + void* userData = nullptr, + bool freeOnError = true) { + return takeOwnership(buf, capacity, capacity, freeFn, userData, + freeOnError); + } + + MemBuf(TakeOwnershipOp op, void* buf, std::size_t capacity, + FreeFunction freeFn = nullptr, void* userData = nullptr, + bool freeOnError = true) + : MemBuf(op, buf, capacity, capacity, freeFn, userData, freeOnError) {} + + static std::unique_ptr takeOwnership(void* buf, std::size_t capacity, + std::size_t length, + FreeFunction freeFn = nullptr, + void* userData = nullptr, + bool freeOnError = true); + + MemBuf(TakeOwnershipOp, void* buf, std::size_t capacity, std::size_t length, + FreeFunction freeFn = nullptr, void* userData = nullptr, + bool freeOnError = true); + + static std::unique_ptr wrapBuffer(const void* buf, + std::size_t capacity); + + static MemBuf wrapBufferAsValue(const void* buf, + std::size_t capacity) noexcept; + + MemBuf(WrapBufferOp op, const void* buf, std::size_t capacity) noexcept; + + /** + * Convenience function to create a new MemBuf object that copies data from a + * user-supplied buffer, optionally allocating a given amount of + * headroom and tailroom. + */ + static std::unique_ptr copyBuffer(const void* buf, std::size_t size, + std::size_t headroom = 0, + std::size_t minTailroom = 0); + + MemBuf(CopyBufferOp op, const void* buf, std::size_t size, + std::size_t headroom = 0, std::size_t minTailroom = 0); + + /** + * Convenience function to free a chain of MemBufs held by a unique_ptr. + */ + static void destroy(std::unique_ptr&& data) { + auto destroyer = std::move(data); + } + + ~MemBuf(); + + bool empty() const; + + const uint8_t* data() const { return data_; } + + uint8_t* writableData() { return data_; } + + const uint8_t* tail() const { return data_ + length_; } + + uint8_t* writableTail() { return data_ + length_; } + + std::size_t length() const { return length_; } + + std::size_t headroom() const { return std::size_t(data_ - buffer()); } + + std::size_t tailroom() const { return std::size_t(bufferEnd() - tail()); } + + const uint8_t* buffer() const { return buf_; } + + uint8_t* writableBuffer() { return buf_; } + + const uint8_t* bufferEnd() const { return buf_ + capacity_; } + + std::size_t capacity() const { return capacity_; } + + MemBuf* next() { return next_; } + + const MemBuf* next() const { return next_; } + + MemBuf* prev() { return prev_; } + + const MemBuf* prev() const { return prev_; } + + /** + * Shift the data forwards in the buffer. + * + * This shifts the data pointer forwards in the buffer to increase the + * headroom. This is commonly used to increase the headroom in a newly + * allocated buffer. + * + * The caller is responsible for ensuring that there is sufficient + * tailroom in the buffer before calling advance(). + * + * If there is a non-zero data length, advance() will use memmove() to shift + * the data forwards in the buffer. In this case, the caller is responsible + * for making sure the buffer is unshared, so it will not affect other MemBufs + * that may be sharing the same underlying buffer. + */ + void advance(std::size_t amount) { + // In debug builds, assert if there is a problem. + assert(amount <= tailroom()); + + if (length_ > 0) { + memmove(data_ + amount, data_, length_); + } + data_ += amount; + } + + /** + * Shift the data backwards in the buffer. + * + * The caller is responsible for ensuring that there is sufficient headroom + * in the buffer before calling retreat(). + * + * If there is a non-zero data length, retreat() will use memmove() to shift + * the data backwards in the buffer. In this case, the caller is responsible + * for making sure the buffer is unshared, so it will not affect other MemBufs + * that may be sharing the same underlying buffer. + */ + void retreat(std::size_t amount) { + // In debug builds, assert if there is a problem. + assert(amount <= headroom()); + + if (length_ > 0) { + memmove(data_ - amount, data_, length_); + } + data_ -= amount; + } + + void prepend(std::size_t amount) { + data_ -= amount; + length_ += amount; + } + + void append(std::size_t amount) { length_ += amount; } + + void trimStart(std::size_t amount) { + data_ += amount; + length_ -= amount; + } + + void trimEnd(std::size_t amount) { length_ -= amount; } + + void clear() { + data_ = writableBuffer(); + length_ = 0; + } + + void reserve(std::size_t minHeadroom, std::size_t minTailroom) { + // Maybe we don't need to do anything. + if (headroom() >= minHeadroom && tailroom() >= minTailroom) { + return; + } + // If the buffer is empty but we have enough total room (head + tail), + // move the data_ pointer around. + if (length() == 0 && headroom() + tailroom() >= minHeadroom + minTailroom) { + data_ = writableBuffer() + minHeadroom; + return; + } + // Bah, we have to do actual work. + reserveSlow(minHeadroom, minTailroom); + } + + bool isChained() const { + assert((next_ == this) == (prev_ == this)); + return next_ != this; + } + + size_t countChainElements() const; + + std::size_t computeChainDataLength() const; + + void prependChain(std::unique_ptr&& iobuf); + + void appendChain(std::unique_ptr&& iobuf) { + // Just use prependChain() on the next element in our chain + next_->prependChain(std::move(iobuf)); + } + + std::unique_ptr unlink() { + next_->prev_ = prev_; + prev_->next_ = next_; + prev_ = this; + next_ = this; + return std::unique_ptr(this); + } + + /** + * Remove this MemBuf from its current chain and return a unique_ptr to + * the MemBuf that formerly followed it in the chain. + */ + std::unique_ptr pop() { + MemBuf* next = next_; + next_->prev_ = prev_; + prev_->next_ = next_; + prev_ = this; + next_ = this; + return std::unique_ptr((next == this) ? nullptr : next); + } + + /** + * Remove a subchain from this chain. + * + * Remove the subchain starting at head and ending at tail from this chain. + * + * Returns a unique_ptr pointing to head. (In other words, ownership of the + * head of the subchain is transferred to the caller.) If the caller ignores + * the return value and lets the unique_ptr be destroyed, the subchain will + * be immediately destroyed. + * + * The subchain referenced by the specified head and tail must be part of the + * same chain as the current MemBuf, but must not contain the current MemBuf. + * However, the specified head and tail may be equal to each other (i.e., + * they may be a subchain of length 1). + */ + std::unique_ptr separateChain(MemBuf* head, MemBuf* tail) { + assert(head != this); + assert(tail != this); + + head->prev_->next_ = tail->next_; + tail->next_->prev_ = head->prev_; + + head->prev_ = tail; + tail->next_ = head; + + return std::unique_ptr(head); + } + + /** + * Return true if at least one of the MemBufs in this chain are shared, + * or false if all of the MemBufs point to unique buffers. + * + * Use isSharedOne() to only check this MemBuf rather than the entire chain. + */ + bool isShared() const { + const MemBuf* current = this; + while (true) { + if (current->isSharedOne()) { + return true; + } + current = current->next_; + if (current == this) { + return false; + } + } + } + + /** + * Return true if all MemBufs in this chain are managed by the usual + * refcounting mechanism (and so the lifetime of the underlying memory + * can be extended by clone()). + */ + bool isManaged() const { + const MemBuf* current = this; + while (true) { + if (!current->isManagedOne()) { + return false; + } + current = current->next_; + if (current == this) { + return true; + } + } + } + + /** + * Return true if this MemBuf is managed by the usual refcounting mechanism + * (and so the lifetime of the underlying memory can be extended by + * cloneOne()). + */ + bool isManagedOne() const { return sharedInfo(); } + + /** + * Return true if other MemBufs are also pointing to the buffer used by this + * MemBuf, and false otherwise. + * + * If this MemBuf points at a buffer owned by another (non-MemBuf) part of the + * code (i.e., if the MemBuf was created using wrapBuffer(), or was cloned + * from such an MemBuf), it is always considered shared. + * + * This only checks the current MemBuf, and not other MemBufs in the chain. + */ + bool isSharedOne() const { + // If this is a user-owned buffer, it is always considered shared + if ((TRANSPORT_EXPECT_FALSE(!sharedInfo()))) { + return true; + } + + if ((TRANSPORT_EXPECT_FALSE(sharedInfo()->externallyShared))) { + return true; + } + + if ((TRANSPORT_EXPECT_TRUE(!(flags() & flag_maybe_shared)))) { + return false; + } + + // flag_maybe_shared is set, so we need to check the reference count. + // (Checking the reference count requires an atomic operation, which is why + // we prefer to only check flag_maybe_shared if possible.) + bool shared = sharedInfo()->refcount.load(std::memory_order_acquire) > 1; + if (!shared) { + // we're the last one left + clearFlags(flag_maybe_shared); + } + return shared; + } + + /** + * Ensure that this MemBuf has a unique buffer that is not shared by other + * MemBufs. + * + * unshare() operates on an entire chain of MemBuf objects. If the chain is + * shared, it may also coalesce the chain when making it unique. If the + * chain is coalesced, subsequent MemBuf objects in the current chain will be + * automatically deleted. + * + * Note that buffers owned by other (non-MemBuf) users are automatically + * considered shared. + * + * Throws std::bad_alloc on error. On error the MemBuf chain will be + * unmodified. + * + * Currently unshare may also throw std::overflow_error if it tries to + * coalesce. (TODO: In the future it would be nice if unshare() were smart + * enough not to coalesce the entire buffer if the data is too large. + * However, in practice this seems unlikely to become an issue.) + */ + void unshare() { + if (isChained()) { + unshareChained(); + } else { + unshareOne(); + } + } + + /** + * Ensure that this MemBuf has a unique buffer that is not shared by other + * MemBufs. + * + * unshareOne() operates on a single MemBuf object. This MemBuf will have a + * unique buffer after unshareOne() returns, but other MemBufs in the chain + * may still be shared after unshareOne() returns. + * + * Throws std::bad_alloc on error. On error the MemBuf will be unmodified. + */ + void unshareOne() { + if (isSharedOne()) { + unshareOneSlow(); + } + } + + /** + * Mark the underlying buffers in this chain as shared with external memory + * management mechanism. This will make isShared() always returns true. + * + * This function is not thread-safe, and only safe to call immediately after + * creating an MemBuf, before it has been shared with other threads. + */ + void markExternallyShared(); + + /** + * Mark the underlying buffer that this MemBuf refers to as shared with + * external memory management mechanism. This will make isSharedOne() always + * returns true. + * + * This function is not thread-safe, and only safe to call immediately after + * creating an MemBuf, before it has been shared with other threads. + */ + void markExternallySharedOne() { + SharedInfo* info = sharedInfo(); + if (info) { + info->externallyShared = true; + } + } + + /** + * Ensure that the memory that MemBufs in this chain refer to will continue to + * be allocated for as long as the MemBufs of the chain (or any clone()s + * created from this point onwards) is alive. + * + * This only has an effect for user-owned buffers (created with the + * WRAP_BUFFER constructor or wrapBuffer factory function), in which case + * those buffers are unshared. + */ + void makeManaged() { + if (isChained()) { + makeManagedChained(); + } else { + makeManagedOne(); + } + } + + /** + * Ensure that the memory that this MemBuf refers to will continue to be + * allocated for as long as this MemBuf (or any clone()s created from this + * point onwards) is alive. + * + * This only has an effect for user-owned buffers (created with the + * WRAP_BUFFER constructor or wrapBuffer factory function), in which case + * those buffers are unshared. + */ + void makeManagedOne() { + if (!isManagedOne()) { + // We can call the internal function directly; unmanaged implies shared. + unshareOneSlow(); + } + } + + // /** + // * Coalesce this MemBuf chain into a single buffer. + // * + // * This method moves all of the data in this MemBuf chain into a single + // * contiguous buffer, if it is not already in one buffer. After coalesce() + // * returns, this MemBuf will be a chain of length one. Other MemBufs in + // the + // * chain will be automatically deleted. + // * + // * After coalescing, the MemBuf will have at least as much headroom as the + // * first MemBuf in the chain, and at least as much tailroom as the last + // MemBuf + // * in the chain. + // * + // * Throws std::bad_alloc on error. On error the MemBuf chain will be + // * unmodified. + // * + // * Returns ByteRange that points to the data MemBuf stores. + // */ + // ByteRange coalesce() { + // const std::size_t newHeadroom = headroom(); + // const std::size_t newTailroom = prev()->tailroom(); + // return coalesceWithHeadroomTailroom(newHeadroom, newTailroom); + // } + + // /** + // * This is similar to the coalesce() method, except this allows to set a + // * headroom and tailroom after coalescing. + // * + // * Returns ByteRange that points to the data MemBuf stores. + // */ + // ByteRange coalesceWithHeadroomTailroom( + // std::size_t newHeadroom, + // std::size_t newTailroom) { + // if (isChained()) { + // coalesceAndReallocate( + // newHeadroom, computeChainDataLength(), this, newTailroom); + // } + // return ByteRange(data_, length_); + // } + + /** + * Ensure that this chain has at least maxLength bytes available as a + * contiguous memory range. + * + * This method coalesces whole buffers in the chain into this buffer as + * necessary until this buffer's length() is at least maxLength. + * + * After coalescing, the MemBuf will have at least as much headroom as the + * first MemBuf in the chain, and at least as much tailroom as the last MemBuf + * that was coalesced. + * + * Throws std::bad_alloc or std::overflow_error on error. On error the MemBuf + * chain will be unmodified. Throws std::overflow_error if maxLength is + * longer than the total chain length. + * + * Upon return, either enough of the chain was coalesced into a contiguous + * region, or the entire chain was coalesced. That is, + * length() >= maxLength || !isChained() is true. + */ + void gather(std::size_t maxLength) { + if (!isChained() || length_ >= maxLength) { + return; + } + coalesceSlow(maxLength); + } + + /** + * Return a new MemBuf chain sharing the same data as this chain. + * + * The new MemBuf chain will normally point to the same underlying data + * buffers as the original chain. (The one exception to this is if some of + * the MemBufs in this chain contain small internal data buffers which cannot + * be shared.) + */ + std::unique_ptr clone() const; + + /** + * Similar to clone(). But returns MemBuf by value rather than heap-allocating + * it. + */ + MemBuf cloneAsValue() const; + + /** + * Return a new MemBuf with the same data as this MemBuf. + * + * The new MemBuf returned will not be part of a chain (even if this MemBuf is + * part of a larger chain). + */ + std::unique_ptr cloneOne() const; + + /** + * Similar to cloneOne(). But returns MemBuf by value rather than + * heap-allocating it. + */ + MemBuf cloneOneAsValue() const; + + /** + * Return a new unchained MemBuf that may share the same data as this chain. + * + * If the MemBuf chain is not chained then the new MemBuf will point to the + * same underlying data buffer as the original chain. Otherwise, it will clone + * and coalesce the MemBuf chain. + * + * The new MemBuf will have at least as much headroom as the first MemBuf in + * the chain, and at least as much tailroom as the last MemBuf in the chain. + * + * Throws std::bad_alloc on error. + */ + std::unique_ptr cloneCoalesced() const; + + /** + * This is similar to the cloneCoalesced() method, except this allows to set a + * headroom and tailroom for the new MemBuf. + */ + std::unique_ptr cloneCoalescedWithHeadroomTailroom( + std::size_t newHeadroom, std::size_t newTailroom) const; + + /** + * Similar to cloneCoalesced(). But returns MemBuf by value rather than + * heap-allocating it. + */ + MemBuf cloneCoalescedAsValue() const; + + /** + * This is similar to the cloneCoalescedAsValue() method, except this allows + * to set a headroom and tailroom for the new MemBuf. + */ + MemBuf cloneCoalescedAsValueWithHeadroomTailroom( + std::size_t newHeadroom, std::size_t newTailroom) const; + + /** + * Similar to Clone(). But use other as the head node. Other nodes in the + * chain (if any) will be allocted on heap. + */ + void cloneInto(MemBuf& other) const { other = cloneAsValue(); } + + /** + * Similar to CloneOne(). But to fill an existing MemBuf instead of a new + * MemBuf. + */ + void cloneOneInto(MemBuf& other) const { other = cloneOneAsValue(); } + + /** + * Return an iovector suitable for e.g. writev() + * + * auto iov = buf->getIov(); + * auto xfer = writev(fd, iov.data(), iov.size()); + * + * Naturally, the returned iovector is invalid if you modify the buffer + * chain. + */ + std::vector getIov() const; + + /** + * Update an existing iovec array with the MemBuf data. + * + * New iovecs will be appended to the existing vector; anything already + * present in the vector will be left unchanged. + * + * Naturally, the returned iovec data will be invalid if you modify the + * buffer chain. + */ + void appendToIov(std::vector* iov) const; + + /** + * Fill an iovec array with the MemBuf data. + * + * Returns the number of iovec filled. If there are more buffer than + * iovec, returns 0. This version is suitable to use with stack iovec + * arrays. + * + * Naturally, the filled iovec data will be invalid if you modify the + * buffer chain. + */ + size_t fillIov(struct iovec* iov, size_t len) const; + + /** + * A helper that wraps a number of iovecs into an MemBuf chain. If count == + * 0, then a zero length buf is returned. This function never returns + * nullptr. + */ + static std::unique_ptr wrapIov(const iovec* vec, size_t count); + + /** + * A helper that takes ownerships a number of iovecs into an MemBuf chain. If + * count == 0, then a zero length buf is returned. This function never + * returns nullptr. + */ + static std::unique_ptr takeOwnershipIov(const iovec* vec, + size_t count, + FreeFunction freeFn = nullptr, + void* userData = nullptr, + bool freeOnError = true); + + /* + * Overridden operator new and delete. + * These perform specialized memory management to help support + * createCombined(), which allocates MemBuf objects together with the buffer + * data. + */ + void* operator new(size_t size); + void* operator new(size_t size, void* ptr); + void operator delete(void* ptr); + void operator delete(void* ptr, void* placement); + + // /** + // * Iteration support: a chain of MemBufs may be iterated through using + // * STL-style iterators over const ByteRanges. Iterators are only + // invalidated + // * if the MemBuf that they currently point to is removed. + // */ + // Iterator cbegin() const; + // Iterator cend() const; + // Iterator begin() const; + // Iterator end() const; + + /** + * Allocate a new null buffer. + * + * This can be used to allocate an empty MemBuf on the stack. It will have no + * space allocated for it. This is generally useful only to later use move + * assignment to fill out the MemBuf. + */ + MemBuf() noexcept; + + /** + * Move constructor and assignment operator. + * + * In general, you should only ever move the head of an MemBuf chain. + * Internal nodes in an MemBuf chain are owned by the head of the chain, and + * should not be moved from. (Technically, nothing prevents you from moving + * a non-head node, but the moved-to node will replace the moved-from node in + * the chain. This has implications for ownership, since non-head nodes are + * owned by the chain head. You are then responsible for relinquishing + * ownership of the moved-to node, and manually deleting the moved-from + * node.) + * + * With the move assignment operator, the destination of the move should be + * the head of an MemBuf chain or a solitary MemBuf not part of a chain. If + * the move destination is part of a chain, all other MemBufs in the chain + * will be deleted. + */ + MemBuf(MemBuf&& other) noexcept; + MemBuf& operator=(MemBuf&& other) noexcept; + + MemBuf(const MemBuf& other); + MemBuf& operator=(const MemBuf& other); + + private: + enum FlagsEnum : uintptr_t { + // Adding any more flags would not work on 32-bit architectures, + // as these flags are stashed in the least significant 2 bits of a + // max-align-aligned pointer. + flag_free_shared_info = 0x1, + flag_maybe_shared = 0x2, + flag_mask = flag_free_shared_info | flag_maybe_shared + }; + + struct SharedInfo { + SharedInfo(); + SharedInfo(FreeFunction fn, void* arg); + + // A pointer to a function to call to free the buffer when the refcount + // hits 0. If this is null, free() will be used instead. + FreeFunction freeFn; + void* userData; + std::atomic refcount; + bool externallyShared{false}; + }; + // Helper structs for use by operator new and delete + struct HeapPrefix; + struct HeapStorage; + struct HeapFullStorage; + + /** + * Create a new MemBuf pointing to an external buffer. + * + * The caller is responsible for holding a reference count for this new + * MemBuf. The MemBuf constructor does not automatically increment the + * reference count. + */ + struct InternalConstructor {}; // avoid conflicts + MemBuf(InternalConstructor, uintptr_t flagsAndSharedInfo, uint8_t* buf, + std::size_t capacity, uint8_t* data, std::size_t length) noexcept; + + void unshareOneSlow(); + void unshareChained(); + void makeManagedChained(); + void coalesceSlow(); + void coalesceSlow(size_t maxLength); + // newLength must be the entire length of the buffers between this and + // end (no truncation) + void coalesceAndReallocate(size_t newHeadroom, size_t newLength, MemBuf* end, + size_t newTailroom); + void coalesceAndReallocate(size_t newLength, MemBuf* end) { + coalesceAndReallocate(headroom(), newLength, end, end->prev_->tailroom()); + } + void decrementRefcount(); + void reserveSlow(std::size_t minHeadroom, std::size_t minTailroom); + void freeExtBuffer(); + + static size_t goodExtBufferSize(std::size_t minCapacity); + static void initExtBuffer(uint8_t* buf, size_t mallocSize, + SharedInfo** infoReturn, + std::size_t* capacityReturn); + static void allocExtBuffer(std::size_t minCapacity, uint8_t** bufReturn, + SharedInfo** infoReturn, + std::size_t* capacityReturn); + static void releaseStorage(HeapStorage* storage, uint16_t freeFlags); + static void freeInternalBuf(void* buf, void* userData); + + /* + * Member variables + */ + + /* + * Links to the next and the previous MemBuf in this chain. + * + * The chain is circularly linked (the last element in the chain points back + * at the head), and next_ and prev_ can never be null. If this MemBuf is the + * only element in the chain, next_ and prev_ will both point to this. + */ + MemBuf* next_{this}; + MemBuf* prev_{this}; + + /* + * A pointer to the start of the data referenced by this MemBuf, and the + * length of the data. + * + * This may refer to any subsection of the actual buffer capacity. + */ + uint8_t* data_{nullptr}; + uint8_t* buf_{nullptr}; + std::size_t length_{0}; + std::size_t capacity_{0}; + + // Pack flags in least significant 2 bits, sharedInfo in the rest + mutable uintptr_t flags_and_shared_info_{0}; + + static inline uintptr_t packFlagsAndSharedInfo(uintptr_t flags, + SharedInfo* info) { + uintptr_t uinfo = reinterpret_cast(info); + return flags | uinfo; + } + + inline SharedInfo* sharedInfo() const { + return reinterpret_cast(flags_and_shared_info_ & ~flag_mask); + } + + inline void setSharedInfo(SharedInfo* info) { + uintptr_t uinfo = reinterpret_cast(info); + flags_and_shared_info_ = (flags_and_shared_info_ & flag_mask) | uinfo; + } + + inline uintptr_t flags() const { return flags_and_shared_info_ & flag_mask; } + + // flags_ are changed from const methods + inline void setFlags(uintptr_t flags) const { + flags_and_shared_info_ |= flags; + } + + inline void clearFlags(uintptr_t flags) const { + flags_and_shared_info_ &= ~flags; + } + + inline void setFlagsAndSharedInfo(uintptr_t flags, SharedInfo* info) { + flags_and_shared_info_ = packFlagsAndSharedInfo(flags, info); + } + + struct DeleterBase { + virtual ~DeleterBase() {} + virtual void dispose(void* p) = 0; + }; + + template + struct UniquePtrDeleter : public DeleterBase { + typedef typename UniquePtr::pointer Pointer; + typedef typename UniquePtr::deleter_type Deleter; + + explicit UniquePtrDeleter(Deleter deleter) : deleter_(std::move(deleter)) {} + void dispose(void* p) override { + try { + deleter_(static_cast(p)); + delete this; + } catch (...) { + abort(); + } + } + + private: + Deleter deleter_; + }; + + static void freeUniquePtrBuffer(void* ptr, void* userData) { + static_cast(userData)->dispose(ptr); + } +}; + +// template +// typename std::enable_if< +// detail::IsUniquePtrToSL::value, +// std::unique_ptr>::type +// MemBuf::takeOwnership(UniquePtr&& buf, size_t count) { +// size_t size = count * sizeof(typename UniquePtr::element_type); +// auto deleter = new UniquePtrDeleter(buf.get_deleter()); +// return takeOwnership( +// buf.release(), size, &MemBuf::freeUniquePtrBuffer, deleter); +// } + +inline std::unique_ptr MemBuf::copyBuffer(const void* data, + std::size_t size, + std::size_t headroom, + std::size_t minTailroom) { + std::size_t capacity = headroom + size + minTailroom; + std::unique_ptr buf = MemBuf::create(capacity); + buf->advance(headroom); + if (size != 0) { + memcpy(buf->writableData(), data, size); + } + buf->append(size); + return buf; +} + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/min_filter.h b/libtransport/src/hicn/transport/utils/min_filter.h new file mode 100755 index 000000000..acb081edc --- /dev/null +++ b/libtransport/src/hicn/transport/utils/min_filter.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License.sudo make instamake install + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +namespace utils { + +template +class MinFilter { + public: + MinFilter(std::size_t size) : size_(size) {} + + std::size_t size() { return by_arrival_.size(); } + + template + TRANSPORT_ALWAYS_INLINE void pushBack(R&& value) { + if (by_arrival_.size() > size_) { + by_order_.erase(by_arrival_.back()); + by_arrival_.pop_back(); + } + + by_arrival_.push_front(by_order_.insert(std::forward(value))); + } + + TRANSPORT_ALWAYS_INLINE const T& begin() { return *by_order_.cbegin(); } + + TRANSPORT_ALWAYS_INLINE const T& rBegin() { return *by_order_.crbegin(); } + + private: + std::multiset by_order_; + std::deque::const_iterator> by_arrival_; + std::size_t size_; +}; + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/object_pool.h b/libtransport/src/hicn/transport/utils/object_pool.h new file mode 100755 index 000000000..d4d8e18d6 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/object_pool.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +// TODO +#include + +#include +#include +#include + +namespace utils { + +template +class ObjectPool { + class ObjectDeleter { + public: + ObjectDeleter(ObjectPool *pool = nullptr) : pool_(pool) {} + + void operator()(T *t) { + if (pool_) { + pool_->add(t); + } else { + delete t; + } + } + + private: + ObjectPool *pool_; + }; + + public: + using Ptr = std::unique_ptr; + + ObjectPool() {} + + std::pair get() { + if (object_pool_.empty()) { + return std::make_pair(false, makePtr(nullptr)); + } + + utils::SpinLock::Acquire locked(object_pool_lock_); + auto ret = std::move(object_pool_.front()); + object_pool_.pop_front(); + return std::make_pair(true, std::move(ret)); + } + + void add(T *object) { + utils::SpinLock::Acquire locked(object_pool_lock_); + object_pool_.emplace_back(makePtr(object)); + } + + Ptr makePtr(T *object) { return Ptr(object, ObjectDeleter(this)); } + + private: + // No copies + ObjectPool(const ObjectPool &other) = delete; + + utils::SpinLock object_pool_lock_; + std::deque object_pool_; +}; + +} // namespace utils \ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/ring_buffer.h b/libtransport/src/hicn/transport/utils/ring_buffer.h new file mode 100755 index 000000000..52bcd81c4 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/ring_buffer.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace utils { + +/** + * NOTE: Single consumer single producer ring buffer + */ +template +class CircularFifo { + public: + enum { Capacity = Size + 1 }; + + CircularFifo() : tail_(0), head_(0), size_(0) {} + virtual ~CircularFifo() {} + + bool push(const Element& item); + bool push(Element&& item); + bool pop(Element& item); + + bool wasEmpty() const; + bool wasFull() const; + bool isLockFree() const; + std::size_t size() const; + + private: + std::size_t increment(std::size_t idx) const; + std::atomic tail_; // tail(input) index + Element array_[Capacity]; + std::atomic head_; // head(output) index + std::atomic size_; +}; + +template +bool CircularFifo::push(const Element& item) { + const auto current_tail = tail_.load(std::memory_order_relaxed); + const auto next_tail = increment(current_tail); + if (next_tail != head_.load(std::memory_order_acquire)) { + array_[current_tail] = item; + tail_.store(next_tail, std::memory_order_release); + size_++; + return true; + } + + // full queue + return false; +} + +/** + * Push by move + */ +template +bool CircularFifo::push(Element&& item) { + const auto current_tail = tail_.load(std::memory_order_relaxed); + const auto next_tail = increment(current_tail); + if (next_tail != head_.load(std::memory_order_acquire)) { + array_[current_tail] = std::move(item); + tail_.store(next_tail, std::memory_order_release); + size_++; + return true; + } + + // full queue + return false; +} + +// Pop by Consumer can only update the head +// (load with relaxed, store with release) +// the tail must be accessed with at least acquire +template +bool CircularFifo::pop(Element& item) { + const auto current_head = head_.load(std::memory_order_relaxed); + if (current_head == tail_.load(std::memory_order_acquire)) { + return false; // empty queue + } + + item = std::move(array_[current_head]); + head_.store(increment(current_head), std::memory_order_release); + size_--; + return true; +} + +template +bool CircularFifo::wasEmpty() const { + // snapshot with acceptance of that this comparison operation is not atomic + return (head_.load() == tail_.load()); +} + +// snapshot with acceptance that this comparison is not atomic +template +bool CircularFifo::wasFull() const { + const auto next_tail = + increment(tail_.load()); // acquire, we dont know who call + return (next_tail == head_.load()); +} + +template +bool CircularFifo::isLockFree() const { + return (tail_.is_lock_free() && head_.is_lock_free()); +} + +template +std::size_t CircularFifo::increment(std::size_t idx) const { + return (idx + 1) % Capacity; +} + +template +std::size_t CircularFifo::size() const { + return size_.load(std::memory_order_relaxed); +} + +} // namespace utils \ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/sharable_vector.h b/libtransport/src/hicn/transport/utils/sharable_vector.h new file mode 100755 index 000000000..31adff1ad --- /dev/null +++ b/libtransport/src/hicn/transport/utils/sharable_vector.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace utils { + +template +class SharableVector : public std::vector, + public std::enable_shared_from_this> { + public: + virtual ~SharableVector(){}; +}; + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/signer.cc b/libtransport/src/hicn/transport/utils/signer.cc new file mode 100755 index 000000000..c11d5e183 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/signer.cc @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright 2017 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + + +extern "C" { +TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat") +#include +#include +#include +#include +} + +#include + +#define ALLOW_UNALIGNED_READS 1 + +namespace utils { + +uint8_t Signer::zeros[200] = {0}; + +/*One signer_ per Private Key*/ +Signer::Signer(PARCKeyStore *keyStore, PARCCryptoSuite suite) { + switch (suite) { + case PARCCryptoSuite_NULL_CRC32C: + break; + case PARCCryptoSuite_ECDSA_SHA256: + case PARCCryptoSuite_RSA_SHA256: + case PARCCryptoSuite_DSA_SHA256: + case PARCCryptoSuite_RSA_SHA512: + this->signer_ = + parcSigner_Create(parcPublicKeySigner_Create(keyStore, suite), + PARCPublicKeySignerAsSigner); + this->key_id_ = parcSigner_CreateKeyId(this->signer_); + break; + + case PARCCryptoSuite_HMAC_SHA512: + case PARCCryptoSuite_HMAC_SHA256: + default: + this->signer_ = parcSigner_Create( + parcSymmetricKeySigner_Create((PARCSymmetricKeyStore *)keyStore, + parcCryptoSuite_GetCryptoHash(suite)), + PARCSymmetricKeySignerAsSigner); + this->key_id_ = parcSigner_CreateKeyId(this->signer_); + break; + } +} + +Signer::Signer(const PARCSigner *signer) + : signer_(parcSigner_Acquire(signer)), + key_id_(parcSigner_CreateKeyId(this->signer_)) {} + +Signer::~Signer() { + parcSigner_Release(&signer_); + parcKeyId_Release(&key_id_); +} + +void Signer::sign(Packet &packet) { + // header chain points to the IP + TCP hicn header + utils::MemBuf *header_chain = packet.header_head_; + utils::MemBuf * payload_chain = packet.payload_head_; + uint8_t *hicn_packet = header_chain->writableData(); + Packet::Format format = packet.getFormat(); + std::size_t sign_len_bytes = parcSigner_GetSignatureSize(signer_); + + if (!(format & HFO_AH)) { + throw errors::MalformedAHPacketException(); + } + + // Copy IP+TCP/ICMP header before zeroing them + hicn_header_t header_copy; + if (format & HFO_INET) { + memcpy(&header_copy, hicn_packet, sizeof(hicn_v4_hdr_t)); + } else if (format & HFO_INET6) { + memcpy(&header_copy, hicn_packet, sizeof(hicn_v6_hdr_t)); + } + + std::size_t header_len = Packet::getHeaderSizeFromFormat(format); + + packet.resetForHash(); + packet.setSignatureSize(sign_len_bytes); + + /* Fill the hicn_ah header */ + using namespace std::chrono; + auto now = duration_cast(system_clock::now().time_since_epoch()).count(); + packet.setSignatureTimestamp(now); + // *reinterpret_cast(ah->signTime) = utils::hton(now); + // // std::memcpy(&ah->hicn_ah.signTime, &sign_time, sizeof(ah->hicn_ah.signTime)); + + packet.setValidationAlgorithm(CryptoSuite(parcSigner_GetCryptoSuite(this->signer_))); + // ah->validationAlgorithm = parcSigner_GetCryptoSuite(this->signer_); + + KeyId key_id; + key_id.first = (uint8_t *)parcBuffer_Overlay((PARCBuffer *) parcKeyId_GetKeyId(this->key_id_), 0); + packet.setKeyId(key_id); + + // memcpy(ah->keyId, + // parcBuffer_Overlay((PARCBuffer *) parcKeyId_GetKeyId(this->key_id_), 0), + // sizeof(_ah_header_t::keyId)); + + // Calculate hash + utils::CryptoHasher hasher(parcSigner_GetCryptoHasher(signer_)); + hasher.init(); + hasher.updateBytes(hicn_packet, header_len); + hasher.updateBytes(zeros, sign_len_bytes); + + for (utils::MemBuf *current = payload_chain; current != header_chain; current = current->next()) { + hasher.updateBytes(current->data(), current->length()); + } + + utils::CryptoHash hash = hasher.finalize(); + + PARCSignature *signature = parcSigner_SignDigest(this->signer_, hash.hash_); + PARCBuffer *buffer = parcSignature_GetSignature(signature); + + PARCByteArray * byte_array = parcBuffer_Array(buffer); + uint8_t * bytes = parcByteArray_Array(byte_array); + size_t bytes_len = parcBuffer_Remaining(buffer); + + if (bytes_len > sign_len_bytes) { + throw errors::MalformedAHPacketException(); + } + + /* Restore the resetted fields */ + if (format & HFO_INET) { + memcpy(hicn_packet, &header_copy, sizeof(hicn_v4_hdr_t)); + } else if (format & HFO_INET6) { + memcpy(hicn_packet, &header_copy, sizeof(hicn_v6_hdr_t)); + } + + int offset = sign_len_bytes - bytes_len; + + std::unique_ptr signature_buffer; + std::unique_ptr tmp_buf = utils::MemBuf::takeOwnership( + bytes, + bytes_len, + bytes_len, + [](void* buf, void* userData){ parcSignature_Release((PARCSignature **)&userData); }, + signature, + true); + + if (offset) { + signature_buffer = utils::MemBuf::create(offset); + memset(signature_buffer->writableData(), 0, offset); + signature_buffer->append(offset); + signature_buffer->appendChain(std::move(tmp_buf)); + } else { + signature_buffer = std::move(tmp_buf); + } + + packet.setSignature(std::move(signature_buffer)); +} + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/signer.h b/libtransport/src/hicn/transport/utils/signer.h new file mode 100755 index 000000000..7b54b63c8 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/signer.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +extern "C" { +#include +#include +#include +#include +} + +namespace utils { + +using Packet = transport::core::Packet; + +/** + * A signer can use a single key (asymmetric or symmetric) to sign a packet. + */ +class Signer { + friend class Identity; + + public: + /** + * Create a Signer + * + * @param keyStore A keystore containing a private key or simmetric key to + * use to sign packet with this Signer. + * @param suite CryptoSuite to use to verify the signature + */ + Signer(PARCKeyStore *keyStore, PARCCryptoSuite suite); + + Signer(const PARCSigner *signer); + + ~Signer(); + + /** + * @brief Sign a packet + * + * This method is general and must be used for Public-private key signature, + * HMAC and CRC. + * + * @param packet A pointer to the header of the packet to sign. Mutable + * field in the packet must be set to 0. + * @param key_id Indentifier of the key to use to generate the signature. + */ + void sign(Packet &packet); + + private: + PARCSigner *signer_; + PARCKeyId *key_id_; + static uint8_t zeros[200]; +}; + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/socket.h b/libtransport/src/hicn/transport/utils/socket.h new file mode 100755 index 000000000..ab8578f91 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/socket.h @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SOCKET_OPTION_GET 0 +#define SOCKET_OPTION_NOT_GET 1 +#define SOCKET_OPTION_SET 2 +#define SOCKET_OPTION_NOT_SET 3 +#define SOCKET_OPTION_DEFAULT 12345 + +#define VOID_HANDLER 0 + +namespace transport { + +namespace transport { + +template +class Socket; +class ConsumerSocket; +class ProducerSocket; + +using Interest = core::Interest; +using ContentObject = core::ContentObject; +using Name = core::Name; +using ContentObjectManifest = core::ManifestInline; +using InterestManifest = core::ManifestInline; +using HashAlgorithm = core::HashAlgorithm; +using CryptoSuite = utils::CryptoSuite; +using Identity = utils::Identity; +using Verifier = utils::Verifier; + +using HicnForwarderPortal = core::HicnForwarderPortal; + +#ifdef __linux__ +#ifndef __ANDROID__ +using RawSocketPortal = core::RawSocketPortal; +#endif +#endif + +#ifdef __vpp__ +using VPPForwarderPortal = core::VPPForwarderPortal; +using BaseSocket = Socket; +using BasePortal = VPPForwarderPortal; +#else +using BaseSocket = Socket; +using BasePortal = HicnForwarderPortal; +#endif + +using PayloadType = core::PayloadType; +using Prefix = core::Prefix; +using Array = utils::Array; + +using ConsumerInterestCallback = + std::function; + +using ConsumerContentCallback = + std::function; + +using ConsumerTimerCallback = + std::function; + +using ProducerContentCallback = std::function; + +using ConsumerContentObjectCallback = + std::function; + +using ConsumerContentObjectVerificationCallback = + std::function; + +using ConsumerManifestCallback = + std::function; + +using ProducerContentObjectCallback = + std::function; + +using ProducerInterestCallback = + std::function; + +using ProducerInterestCallback = + std::function; + +template +class Socket { + static_assert(std::is_same::value +#ifdef __linux__ +#ifndef __ANDROID__ + || std::is_same::value +#ifdef __vpp__ + || std::is_same::value +#endif +#endif + , +#else + , + +#endif + "This class is not allowed as Portal"); + + public: + typedef PortalType Portal; + + virtual asio::io_service &getIoService() = 0; + + virtual void connect() = 0; + + virtual int setSocketOption(int socket_option_key, + uint32_t socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + double socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + bool socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + Name socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + std::list socket_option_value) = 0; + + virtual int setSocketOption( + int socket_option_key, + ProducerContentObjectCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ProducerInterestCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ProducerContentCallback socket_option_value) = 0; + + virtual int setSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback socket_option_value) = 0; + + virtual int setSocketOption( + int socket_option_key, + ConsumerContentObjectCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ConsumerInterestCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ConsumerContentCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ConsumerManifestCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + IcnObserver *socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + HashAlgorithm socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + CryptoSuite socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + const Identity &socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ConsumerTimerCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + const std::string &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + uint32_t &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + double &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + bool &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + Name &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + std::list &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, + ProducerContentObjectCallback &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, ProducerInterestCallback &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, + ConsumerContentObjectCallback &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, ConsumerInterestCallback &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + ConsumerContentCallback &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, ConsumerManifestCallback &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + ProducerContentCallback &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + std::shared_ptr &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + IcnObserver **socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + HashAlgorithm &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + CryptoSuite &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + Identity &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + std::string &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + ConsumerTimerCallback &socket_option_value) = 0; + + protected: + virtual ~Socket(){}; + + protected: + std::string output_interface_; +}; + +} // namespace transport + +} // namespace transport diff --git a/libtransport/src/hicn/transport/utils/spinlock.h b/libtransport/src/hicn/transport/utils/spinlock.h new file mode 100755 index 000000000..33e5cda85 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/spinlock.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace utils { + +class SpinLock : private std::atomic_flag { + public: + class Acquire { + public: + Acquire(SpinLock& spin_lock) : spin_lock_(spin_lock) { spin_lock_.lock(); } + + ~Acquire() { spin_lock_.unlock(); } + + // No copies + Acquire& operator=(const Acquire&) = delete; + Acquire(const Acquire&) = delete; + + private: + SpinLock& spin_lock_; + }; + + SpinLock() : std::atomic_flag(false) {} + + void lock() { + // busy-wait + while (std::atomic_flag::test_and_set(std::memory_order_acquire)) + ; + } + + void unlock() { clear(std::memory_order_release); } + + bool tryLock() { + return std::atomic_flag::test_and_set(std::memory_order_acquire); + } +}; + +} // namespace utils \ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/stream_buffer.h b/libtransport/src/hicn/transport/utils/stream_buffer.h new file mode 100755 index 000000000..adfb696f2 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/stream_buffer.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace utils { + +template +struct ostreambuf + : public std::basic_streambuf > { + ostreambuf(char_type* buffer, std::streamsize buffer_length) { + // set the "put" pointer the start of the buffer and record it's length. + setp(buffer, buffer + buffer_length); + } +}; + +} // namespace utils \ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/string_tokenizer.cc b/libtransport/src/hicn/transport/utils/string_tokenizer.cc new file mode 100755 index 000000000..9d1911080 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/string_tokenizer.cc @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace utils { + +StringTokenizer::StringTokenizer(const std::string &str) + : str_(str), delimiter_(" ") {} + +StringTokenizer::StringTokenizer(const std::string &str, + const std::string &delim) + : str_(str), delimiter_(delim) {} + +bool StringTokenizer::hasMoreTokens() { + return str_.find(delimiter_) != std::string::npos || !str_.empty(); +} + +std::string StringTokenizer::nextToken() { + unsigned long pos = str_.find(delimiter_); + + bool token_found = std::string::npos != pos; + + if (!token_found && str_.empty()) { + throw errors::TokenizerException(); + } + + std::string token = str_.substr(0, pos); + str_.erase(0, token_found ? pos + delimiter_.length() : pos); + + return token; +} + +} // namespace utils \ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/string_tokenizer.h b/libtransport/src/hicn/transport/utils/string_tokenizer.h new file mode 100755 index 000000000..36630eb58 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/string_tokenizer.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace utils { + +class StringTokenizer { + public: + StringTokenizer(const std::string &str); + StringTokenizer(const std::string &str, const std::string &delim); + + bool hasMoreTokens(); + std::string nextToken(); + + private: + std::string str_; + std::string delimiter_; +}; + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/test.h b/libtransport/src/hicn/transport/utils/test.h new file mode 100755 index 000000000..e3dd619ac --- /dev/null +++ b/libtransport/src/hicn/transport/utils/test.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace testing { + +namespace internal { + +enum GTestColor { COLOR_DEFAULT, COLOR_RED, COLOR_GREEN, COLOR_YELLOW }; + +extern void ColoredPrintf(GTestColor color, const char *fmt, ...); + +} // namespace internal + +} // namespace testing + +#define PRINTF(...) \ + do { \ + testing::internal::ColoredPrintf(testing::internal::COLOR_GREEN, \ + "[ ] "); \ + testing::internal::ColoredPrintf(testing::internal::COLOR_YELLOW, \ + __VA_ARGS__); \ + } while (0) + +// C++ stream interface +class TestCout : public std::stringstream { + public: + ~TestCout() {} +}; + +#define TEST_COUT TestCout() \ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/uri.cc b/libtransport/src/hicn/transport/utils/uri.cc new file mode 100755 index 000000000..33eb8b45b --- /dev/null +++ b/libtransport/src/hicn/transport/utils/uri.cc @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace utils { + +Uri::Uri() {} + +Uri &Uri::parse(const std::string &uri) { + if (uri.length() == 0) { + throw errors::RuntimeException("Malformed URI."); + } + + iterator_t uri_end = uri.end(); + + // get query start + iterator_t query_start = std::find(uri.begin(), uri_end, '?'); + + // protocol + iterator_t protocol_start = uri.begin(); + iterator_t protocol_end = std::find(protocol_start, uri_end, ':'); //"://"); + + if (protocol_end != uri_end) { + std::string prot = &*(protocol_end); + if ((prot.length() > 3) && (prot.substr(0, 3) == "://")) { + protocol_ = std::string(protocol_start, protocol_end); + protocol_end += 3; // :// + } else { + protocol_end = uri.begin(); // no protocol + } + } else { + protocol_end = uri.begin(); // no protocol + } + // host + iterator_t host_start = protocol_end; + iterator_t path_start = + std::find(host_start, uri_end, '/'); // get path_start + + iterator_t host_end = std::find( + protocol_end, (path_start != uri_end) ? path_start : query_start, + ':'); // check for port + + locator_ = std::string(host_start, host_end); + + // port + if ((host_end != uri_end) && ((&*(host_end))[0] == ':')) { + host_end++; + iterator_t port_end = (path_start != uri_end) ? path_start : query_start; + port_ = std::string(host_end, port_end); + } + + // path + if (path_start != uri_end) { + path_ = std::string(path_start, query_start); + } + // query + if (query_start != uri_end) { + query_string_ = std::string(query_start, uri.end()); + } + + return *this; +} + +Uri &Uri::parseProtocolAndLocator(const std::string &locator) { + iterator_t total_end = locator.end(); + + // protocol + iterator_t protocol_start = locator.begin(); + iterator_t protocol_end = + std::find(protocol_start, total_end, ':'); //"://"); + + if (protocol_end != total_end) { + std::string prot = &*(protocol_end); + if ((prot.length() > 3) && (prot.substr(0, 3) == "://")) { + protocol_ = std::string(protocol_start, protocol_end); + protocol_end += 3; // :// + } else { + throw errors::RuntimeException("Malformed locator. (Missing \"://\")"); + } + } else { + throw errors::RuntimeException("Malformed locator. No protocol specified."); + } + + // locator + iterator_t host_start = protocol_end; + iterator_t host_end = std::find(protocol_end, total_end, '/'); + + if (host_start == host_end) { + throw errors::RuntimeException( + "Malformed locator. Locator name is missing"); + } + + locator_ = std::string(host_start, host_end); + + return *this; +} + +std::string Uri::getLocator() { return locator_; } + +std::string Uri::getPath() { return path_; } + +std::string Uri::getPort() { return port_; } + +std::string Uri::getProtocol() { return protocol_; } + +std::string Uri::getQueryString() { return query_string_; } + +} // end namespace utils diff --git a/libtransport/src/hicn/transport/utils/uri.h b/libtransport/src/hicn/transport/utils/uri.h new file mode 100755 index 000000000..7c28e8552 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/uri.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include // find +#include + +namespace utils { + +class Uri { + typedef std::string::const_iterator iterator_t; + + public: + Uri(); + + Uri &parse(const std::string &uri); + + Uri &parseProtocolAndLocator(const std::string &locator); + + std::string getQueryString(); + + std::string getPath(); + + std::string getProtocol(); + + std::string getLocator(); + + std::string getPort(); + + private: + std::string query_string_, path_, protocol_, locator_, port_; +}; // uri + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/verifier.cc b/libtransport/src/hicn/transport/utils/verifier.cc new file mode 100755 index 000000000..9a3de43c1 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/verifier.cc @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +extern "C" { +TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat") +#include +#include +#include +#include +} + +#include + +namespace utils { + +TRANSPORT_ALWAYS_INLINE bool file_exists(const std::string &name) { + struct stat buffer; + return (stat(name.c_str(), &buffer) == 0); +} + +uint8_t Verifier::zeros[200] = {0}; + +Verifier::Verifier() { + parcSecurity_Init(); + PARCInMemoryVerifier *in_memory_verifier = parcInMemoryVerifier_Create(); + this->verifier_ = + parcVerifier_Create(in_memory_verifier, PARCInMemoryVerifierAsVerifier); + parcInMemoryVerifier_Release(&in_memory_verifier); +} + +Verifier::~Verifier() { + parcVerifier_Release(&verifier_); + parcSecurity_Fini(); +} + +/* + * TODO: Unsupported in libparc + */ +bool Verifier::hasKey(PARCKeyId *keyId) { return false; } + +/* + * TODO: signal errors without trap. + */ +bool Verifier::addKey(PARCKey *key) { + parcVerifier_AddKey(this->verifier_, key); + return true; +} + +PARCKeyId * Verifier::addKeyFromCertificate(const std::string &file_name) { + PARCCertificateFactory *factory = parcCertificateFactory_Create(PARCCertificateType_X509, + PARCContainerEncoding_PEM); + parcAssertNotNull(factory, "Expected non-NULL factory"); + + if (!file_exists(file_name)) { + TRANSPORT_LOGW("Warning! The certificate %s file does not exist", + file_name.c_str()); + return nullptr; + } + + PARCCertificate *certificate = + parcCertificateFactory_CreateCertificateFromFile( + factory, (char *)file_name.c_str(), NULL); + + PARCKey *key = parcCertificate_GetPublicKey(certificate); + addKey(key); + + PARCKeyId *ret = parcKeyId_Acquire(parcKey_GetKeyId(key)); + + // parcKey_Release(&key); + // parcCertificate_Release(&certificate); + // parcCertificateFactory_Release(&factory); + + return ret; +} + +int Verifier::verify(const Packet &packet) { + bool valid = false; + + // header chain points to the IP + TCP hicn header + utils::MemBuf *header_chain = packet.header_head_; + utils::MemBuf *payload_chain = packet.payload_head_; + uint8_t *hicn_packet = header_chain->writableData(); + Packet::Format format = packet.getFormat(); + + if (!(packet.format_ & HFO_AH)) { + throw errors::MalformedAHPacketException(); + } + + // Copy IP+TCP/ICMP header before zeroing them + hicn_header_t header_copy; + if (format & HFO_INET) { + memcpy(&header_copy, hicn_packet, sizeof(hicn_v4_hdr_t)); + } else if (format & HFO_INET6) { + memcpy(&header_copy, hicn_packet, sizeof(hicn_v6_hdr_t)); + } + + std::size_t header_len = Packet::getHeaderSizeFromFormat(format); + + PARCCryptoSuite suite = + static_cast(packet.getValidationAlgorithm()); + KeyId _key_id = packet.getKeyId(); + PARCBuffer *buffer = + parcBuffer_Wrap(_key_id.first, _key_id.second, 0, _key_id.second); + PARCKeyId *key_id = parcKeyId_Create(buffer); + parcBuffer_Release(&buffer); + + int ah_payload_len = header_chain->next()->length(); + uint8_t *signature = header_chain->next()->writableData(); + + // Reset fields that should not appear in the signature + const_cast(packet).resetForHash(); + + PARCCryptoHashType hashtype = parcCryptoSuite_GetCryptoHash(suite); + utils::CryptoHasher hasher( + parcVerifier_GetCryptoHasher(verifier_, key_id, hashtype)); + + hasher.init() + .updateBytes(hicn_packet, header_len) + .updateBytes(zeros, ah_payload_len); + + for (utils::MemBuf *current = payload_chain; current != header_chain; + current = current->next()) { + hasher.updateBytes(current->data(), current->length()); + } + + utils::CryptoHash hash = hasher.finalize(); + PARCCryptoHash *hash_computed_locally = hash.hash_; + + PARCBuffer *bits = + parcBuffer_Wrap(signature, ah_payload_len, 0, ah_payload_len); + parcBuffer_Rewind(bits); + + /* IF the signature algo is ECDSA, the signature might be shorter than the + * signature field */ + PARCSigningAlgorithm algo = parcCryptoSuite_GetSigningAlgorithm(suite); + while (algo == PARCSigningAlgorithm_ECDSA && parcBuffer_HasRemaining(bits) && + parcBuffer_GetUint8(bits) == 0) + ; + + if (algo == PARCSigningAlgorithm_ECDSA) { + parcBuffer_SetPosition(bits, parcBuffer_Position(bits) - 1); + } + + if (!parcBuffer_HasRemaining(bits)) { + parcKeyId_Release(&key_id); + parcBuffer_Release(&bits); + return valid; + } + + PARCSignature *signatureToVerify = parcSignature_Create( + parcCryptoSuite_GetSigningAlgorithm(suite), hashtype, bits); + + if (algo == PARCSigningAlgorithm_RSA) { + parcBuffer_SetPosition(bits, 0); + } + + valid = parcVerifier_VerifyDigestSignature( + verifier_, key_id, hash_computed_locally, suite, signatureToVerify); + + /* Restore the resetted fields */ + if (format & HFO_INET) { + memcpy(hicn_packet, &header_copy, sizeof(hicn_v4_hdr_t)); + } else if (format & HFO_INET6) { + memcpy(hicn_packet, &header_copy, sizeof(hicn_v6_hdr_t)); + } + + parcKeyId_Release(&key_id); + + parcBuffer_Release(&bits); + parcSignature_Release(&signatureToVerify); + + return valid; +} +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/verifier.h b/libtransport/src/hicn/transport/utils/verifier.h new file mode 100755 index 000000000..6313a7240 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/verifier.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +extern "C" { +#include +#include +} + +namespace utils { + +using Packet = transport::core::Packet; + +/** + * A verifier holds a crypto cache that contains all the keys to use for + * verify signatures/hmacs. + */ +class Verifier { + public: + Verifier(); + + ~Verifier(); + + /** + * @brief Check if a key is already in this Verifier. + * + * A PARCVerifier contains a CryptoCache with a set of key to use for + * verification purposes. + * + * @param keyId Identifier of the key to match in the CryptoCache of the + * Verifier. + * @return true if the key is found, false otherwise. + */ + bool hasKey(PARCKeyId *keyId); + + /** + * @brief Add a key to this Verifier + * + * @param key to add + * @return true if the key was added successfully, false otherwise. + */ + bool addKey(PARCKey *key); + + PARCKeyId *addKeyFromCertificate(const std::string &file_name); + + /** + * @brief Verify a Signature + * + * This method is general and must be used for Public-private key signature, + * HMAC and CRC. + * + * @param signature A pointer to the buffer holding the signature + * @param sign_len Lenght of the signature (must be consistent with the type + * of the key) + * @param bufferSigned A pointer to the packet header signed with + * signature. Mutable fields and the signature field in the packet must be + * set to 0 + * @param buf_len Lenght of bufferSigned + * @param suite CryptoSuite to use to verify the signature + * @param key_id Indentifier of the key to use to verify the signature. The + * key must be already present in the Verifier. + */ + int verify(const Packet &packet); + + private: + PARCVerifier *verifier_; + static uint8_t zeros[200]; +}; + +} // namespace utils diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt new file mode 100755 index 000000000..95fdd508d --- /dev/null +++ b/utils/CMakeLists.txt @@ -0,0 +1,54 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) +set(CMAKE_CXX_STANDARD 14) + +project(Utils) + +set(CMAKE_MODULE_PATH + ${CMAKE_MODULE_PATH} + "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/Modules" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules" +) + +include(BuildMacros) +include(Packager) + +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + find_package(Libtransport REQUIRED) +else() + # TODO Set name of targets in CMakeroot file + set(LIBTRANSPORT_LIBRARIES ${LIBTRANSPORT}) +endif() + +set (COMPILER_DEFINITIONS "-DASIO_STANDALONE") + +list(APPEND UTILS_SRC + src/hiperf.cc + src/ping_client.cc + src/ping_server.cc +) + +foreach(util ${UTILS_SRC}) + get_filename_component(_util_name ${util} NAME) + string(REGEX REPLACE ".cc" "" util_name ${_util_name}) + + build_executable(${util_name} + SOURCES ${util} + LINK_LIBRARIES ${LIBTRANSPORT_LIBRARIES} + DEPENDS transport + COMPONENT utils + DEFINITIONS ${COMPILER_DEFINITIONS} + ) +endforeach() \ No newline at end of file diff --git a/utils/src/hiperf.cc b/utils/src/hiperf.cc new file mode 100755 index 000000000..d8953b36a --- /dev/null +++ b/utils/src/hiperf.cc @@ -0,0 +1,724 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include + +#ifdef __linux__ +#include +#endif + +namespace transport { + +namespace interface { + +#define ERROR_SUCCESS 0 +#define ERROR_SETUP -5 + +using CryptoSuite = utils::CryptoSuite; +using Identity = utils::Identity; + +struct ClientConfiguration { + ClientConfiguration() + : name("b001::abcd", 0), + verify(false), + beta(-1.f), + drop_factor(-1.f), + window(-1), + virtual_download(true), + producer_certificate("/tmp/rsa_certificate.pem"), + receive_buffer(std::make_shared>()), + download_size(0), + report_interval_milliseconds_(1000), + rtc_(false) {} + + Name name; + bool verify; + double beta; + double drop_factor; + double window; + bool virtual_download; + std::string producer_certificate; + std::shared_ptr> receive_buffer; + std::size_t download_size; + std::uint32_t report_interval_milliseconds_; + TransportProtocolAlgorithms transport_protocol_; + bool rtc_; +}; + +struct ServerConfiguration { + ServerConfiguration() + : name("b001::abcd/64"), + virtual_producer(true), + manifest(false), + live_production(false), + sign(false), + content_lifetime(600000000_U32), + content_object_size(1440), + download_size(20 * 1024 * 1024), + hash_algorithm(HashAlgorithm::SHA_256), + keystore_name("/tmp/rsa_crypto_material.p12"), + keystore_password("cisco"), + multiphase_produce_(false) {} + + Prefix name; + bool virtual_producer; + bool manifest; + bool live_production; + bool sign; + std::uint32_t content_lifetime; + std::uint16_t content_object_size; + std::uint32_t download_size; + HashAlgorithm hash_algorithm; + std::string keystore_name; + std::string keystore_password; + bool multiphase_produce_; +}; + +class HIperfClient { + typedef std::chrono::time_point Time; + typedef std::chrono::microseconds TimeDuration; + + public: + HIperfClient(const ClientConfiguration &conf) + : configuration_(conf), + total_duration_milliseconds_(0), + old_bytes_value_(0) {} + + void processPayload(ConsumerSocket &c, std::size_t bytes_transferred, + const std::error_code &ec) { + Time t2 = std::chrono::steady_clock::now(); + TimeDuration dt = std::chrono::duration_cast(t2 - t1_); + long usec = dt.count(); + + std::cout << "Content retrieved. Size: " << bytes_transferred << " [Bytes]" + << std::endl; + + std::cerr << "Elapsed Time: " << usec / 1000000.0 << " seconds -- " + << (bytes_transferred * 8) * 1.0 / usec * 1.0 << " [Mbps]" + << std::endl; + } + + bool verifyData(ConsumerSocket &c, const ContentObject &contentObject) { + if (contentObject.getPayloadType() == PayloadType::CONTENT_OBJECT) { + std::cout << "VERIFY CONTENT" << std::endl; + } else if (contentObject.getPayloadType() == PayloadType::MANIFEST) { + std::cout << "VERIFY MANIFEST" << std::endl; + } + + return true; + } + + void processLeavingInterest(ConsumerSocket &c, const Interest &interest) { + // std::cout << "LEAVES " << interest.getName().toUri() << std::endl; + } + + void handleTimerExpiration(ConsumerSocket &c, std::size_t byte_count, + std::chrono::milliseconds &exact_duration, + float c_window, uint32_t retransmissions, + uint32_t average_rtt) { + const char separator = ' '; + const int width = 20; + + std::stringstream interval; + interval << total_duration_milliseconds_ / 1000 << "-" + << total_duration_milliseconds_ / 1000 + + exact_duration.count() / 1000; + + std::stringstream bytes_transferred; + bytes_transferred << std::fixed << std::setprecision(3) + << (byte_count - old_bytes_value_) / 1000000.0 + << std::setfill(separator) << "[MBytes]"; + + std::stringstream bandwidth; + bandwidth << ((byte_count - old_bytes_value_) * 8) / + (exact_duration.count()) / 1000.0 + << std::setfill(separator) << "[Mbps]"; + + std::stringstream window; + window << c_window << std::setfill(separator) << "[Interest]"; + + std::stringstream avg_rtt; + avg_rtt << average_rtt << std::setfill(separator) << "[us]"; + + std::cout << std::left << std::setw(width) << "Interval"; + std::cout << std::left << std::setw(width) << "Transfer"; + std::cout << std::left << std::setw(width) << "Bandwidth"; + std::cout << std::left << std::setw(width) << "Retr"; + std::cout << std::left << std::setw(width) << "Cwnd"; + std::cout << std::left << std::setw(width) << "AvgRtt" << std::endl; + + std::cout << std::left << std::setw(width) << interval.str(); + std::cout << std::left << std::setw(width) << bytes_transferred.str(); + std::cout << std::left << std::setw(width) << bandwidth.str(); + std::cout << std::left << std::setw(width) << retransmissions; + std::cout << std::left << std::setw(width) << window.str(); + std::cout << std::left << std::setw(width) << avg_rtt.str() << std::endl; + std::cout << std::endl; + + total_duration_milliseconds_ += exact_duration.count(); + old_bytes_value_ = byte_count; + } + + int setup() { + int ret; + + // Set the transport algorithm + TransportProtocolAlgorithms transport_protocol; + + if (configuration_.rtc_) { + transport_protocol = RTC; + } else if (configuration_.window < 0) { + transport_protocol = RAAQM; + } else { + transport_protocol = CBR; + } + + consumer_socket_ = std::make_unique(transport_protocol); + +#if defined(DEBUG) && defined(__linux__) + std::shared_ptr portal; + consumer_socket_->getSocketOption(GeneralTransportOptions::PORTAL, portal); + signals_ = + std::make_unique(portal->getIoService(), SIGUSR1); + signals_->async_wait([this](const std::error_code &, const int &) { + std::cout << "Signal SIGUSR1!" << std::endl; + mtrace(); + }); +#endif + + if (consumer_socket_->setSocketOption(CURRENT_WINDOW_SIZE, + configuration_.window) == + SOCKET_OPTION_NOT_SET) { + std::cerr << "ERROR -- Impossible to set the size of the window." + << std::endl; + return ERROR_SETUP; + } + + if (transport_protocol == RAAQM && configuration_.beta != -1.f) { + if (consumer_socket_->setSocketOption(RaaqmTransportOptions::BETA_VALUE, + configuration_.beta) == + SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + } + + if (transport_protocol == RAAQM && configuration_.drop_factor != -1.f) { + if (consumer_socket_->setSocketOption(RaaqmTransportOptions::DROP_FACTOR, + configuration_.drop_factor) == + SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + } + + if (consumer_socket_->setSocketOption(OtherOptions::VIRTUAL_DOWNLOAD, + false) == SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + + if (configuration_.verify) { + if (consumer_socket_->setSocketOption( + GeneralTransportOptions::CERTIFICATE, + configuration_.producer_certificate) == SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + } + + if (consumer_socket_->setSocketOption( + GeneralTransportOptions::VERIFY_SIGNATURE, configuration_.verify) == + SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + + ret = consumer_socket_->setSocketOption( + ConsumerCallbacksOptions::INTEREST_OUTPUT, + (ConsumerInterestCallback)std::bind( + &HIperfClient::processLeavingInterest, this, std::placeholders::_1, + std::placeholders::_2)); + + if (ret == SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + + ret = consumer_socket_->setSocketOption( + ConsumerCallbacksOptions::CONTENT_RETRIEVED, + (ConsumerContentCallback)std::bind( + &HIperfClient::processPayload, this, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3)); + + if (ret == SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + + ret = consumer_socket_->setSocketOption( + ConsumerCallbacksOptions::TIMER_EXPIRES, + (ConsumerTimerCallback)std::bind( + &HIperfClient::handleTimerExpiration, this, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, + std::placeholders::_5, std::placeholders::_6)); + + if (ret == SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + + if (consumer_socket_->setSocketOption( + GeneralTransportOptions::TIMER_INTERVAL, + configuration_.report_interval_milliseconds_) == + SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + + consumer_socket_->connect(); + + return ERROR_SUCCESS; + } + + int run() { + std::cout << "Starting download of " << configuration_.name << std::endl; + + do { + t1_ = std::chrono::steady_clock::now(); + consumer_socket_->consume(configuration_.name, + *configuration_.receive_buffer); + } while (configuration_.virtual_download); + + return ERROR_SUCCESS; + } + + private: + ClientConfiguration configuration_; + std::unique_ptr consumer_socket_; + Time t1_; + uint32_t total_duration_milliseconds_; + uint64_t old_bytes_value_; + // std::unique_ptr signals_; +}; + +class HIperfServer { + const std::size_t log2_content_object_buffer_size = 8; + + public: + HIperfServer(ServerConfiguration &conf) + : configuration_(conf), + // signals_(io_service_, SIGINT, SIGQUIT), + content_objects_((1 << log2_content_object_buffer_size)), + content_objects_index_(0), + mask_((1 << log2_content_object_buffer_size) - 1) { + // signals_.async_wait([this] (const std::error_code&, const int&) + // {std::cout << "STOPPING!!" << std::endl; io_service_.stop();}); + + std::string buffer(1440, 'X'); + + std::cout << "Producing contents under name " << conf.name.getName() + << std::endl; + + for (int i = 0; i < (1 << log2_content_object_buffer_size); i++) { + content_objects_[i] = std::make_shared( + conf.name.getName(), HF_INET6_TCP, (const uint8_t *)buffer.data(), + buffer.size()); + content_objects_[i]->setLifetime( + default_values::content_object_expiry_time); + } + } + + void processInterest(ProducerSocket &p, const Interest &interest) { + content_objects_[content_objects_index_ & mask_]->setName( + interest.getName()); + + // if (final_chunk_number_ > 0 && interest.getName().getSuffix() == 0) { + // auto name = interest.getName(); + // manifest_ = std::make_shared(name); + // // manifest_->setFinalChunkNumber(final_chunk_number_); + // manifest_->encode(); + // p.produce(*manifest_); + // return; + // } + + producer_socket_->produce( + *content_objects_[content_objects_index_++ & mask_]); + } + + void processInterest2(ProducerSocket &p, const Interest &interest) { + producer_socket_->setSocketOption(ProducerCallbacksOptions::CACHE_MISS, + (ProducerInterestCallback)VOID_HANDLER); + producer_socket_->setSocketOption( + GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME, 5000_U32); + produceContent(interest.getName().getSuffix()); + producer_socket_->setSocketOption( + ProducerCallbacksOptions::CACHE_MISS, + (ProducerInterestCallback)bind(&HIperfServer::processInterest2, this, + std::placeholders::_1, + std::placeholders::_2)); + } + + void produceContent(uint32_t suffix) { + core::Name name = configuration_.name.getName(); + + std::string content(configuration_.download_size, '?'); + uint32_t total; + + total = producer_socket_->produce( + name, reinterpret_cast(content.data()), content.size(), + !configuration_.multiphase_produce_); + + std::cout << "Written " << total << "pieces of data in output buffer" + << std::endl; + } + + utils::Identity setProducerIdentity(std::string &keystore_name, + std::string &keystore_password, + HashAlgorithm &hash_algorithm) { + if (access(keystore_name.c_str(), F_OK) != -1) { + return utils::Identity(keystore_name, keystore_password, hash_algorithm); + } else { + return utils::Identity(keystore_name, keystore_password, + CryptoSuite::RSA_SHA256, 1024, 365, + "producer-test"); + } + } + + int setup() { + int ret; + + producer_socket_ = std::make_unique(); + + if (configuration_.sign) { + Identity identity = setProducerIdentity(configuration_.keystore_name, + configuration_.keystore_password, + configuration_.hash_algorithm); + + if (producer_socket_->setSocketOption(GeneralTransportOptions::IDENTITY, + identity) == + SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + } + + producer_socket_->registerPrefix(configuration_.name); + + if (!configuration_.virtual_producer) { + if (producer_socket_->setSocketOption( + GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME, + configuration_.content_lifetime) == SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + + if (producer_socket_->setSocketOption( + GeneralTransportOptions::MAKE_MANIFEST, + configuration_.manifest) == SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + + if (producer_socket_->setSocketOption( + GeneralTransportOptions::OUTPUT_BUFFER_SIZE, 200000U) == + SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + + if (!configuration_.live_production) { + produceContent(0); + } else { + ret = producer_socket_->setSocketOption( + ProducerCallbacksOptions::CACHE_MISS, + (ProducerInterestCallback)bind(&HIperfServer::processInterest2, + this, std::placeholders::_1, + std::placeholders::_2)); + + if (ret == SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + } + } else { + ret = producer_socket_->setSocketOption( + GeneralTransportOptions::OUTPUT_BUFFER_SIZE, 0U); + + if (ret == SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + + ret = producer_socket_->setSocketOption( + ProducerCallbacksOptions::CACHE_MISS, + (ProducerInterestCallback)bind(&HIperfServer::processInterest, this, + std::placeholders::_1, + std::placeholders::_2)); + + if (ret == SOCKET_OPTION_NOT_SET) { + return ERROR_SETUP; + } + } + + producer_socket_->connect(); + + return ERROR_SUCCESS; + } + + int run() { + std::cerr << "Starting to serve consumers" << std::endl; + producer_socket_->serveForever(); + + return ERROR_SUCCESS; + } + + private: + ServerConfiguration configuration_; + std::unique_ptr producer_socket_; + // asio::signal_set signals_; + std::vector> content_objects_; + std::uint16_t content_objects_index_; + std::uint16_t mask_; +}; + +void usage() { + std::cerr << std::endl; + std::cerr << "HIPERF - A tool for performing network throughput " + "measurements with hICN" + << std::endl; + std::cerr << "usage: hiperf [-S|-C] [options] [prefix|name]" << std::endl; + std::cerr << "Server or Client:" << std::endl; + std::cerr << "-D\t\t\t\t\t" + << "Run as a daemon" << std::endl; + std::cerr << std::endl; + std::cerr << "Server specific:" << std::endl; + std::cerr << "-s\t\t\t\tSize of the content to publish" + << std::endl; + std::cerr << "-r\t\t\t\t\t" + << "Produce real content of content_size bytes" << std::endl; + std::cerr << "-m\t\t\t\t\t" + << "Produce transport manifest" << std::endl; + std::cerr << "-l\t\t\t\t\t" + << "Start producing content upon the reception of the " + "first interest" + << std::endl; + std::cerr << "-k\t\t\t\t" + << "Path of p12 file containing the " + "crypto material used for signing the packets" + << std::endl; + std::cerr << "-y\t\t\t" + << "Use the selected hash algorithm for " + "calculating manifest digests" + << std::endl; + std::cerr << "-p\t\t\t\t" + << "Password for p12 keystore" << std::endl; + std::cerr << std::endl; + std::cerr << "Client specific:" << std::endl; + std::cerr << "-b\t\t\t" + << "RAAQM beta parameter" << std::endl; + std::cerr << "-d\t\t\t" + << "RAAQM drop factor " + "parameter" + << std::endl; + std::cerr << "-W\t\t\t\t" + << "Use a fixed congestion window " + "for retrieving the data." + << std::endl; + std::cerr << "-c\t\t\t" + << "Path of the producer certificate " + "to be used for verifying the " + "origin of the packets received" + << std::endl; + std::cout << "-v\t\t\t\t\t" + << "Enable verification of received data" << std::endl; +} + +int main(int argc, char *argv[]) { + // Common + bool daemon = false; + + // -1 server, 0 undefined, 1 client + int role = 0; + int options = 0; + + char *log_file = nullptr; + + // Consumer + ClientConfiguration client_configuration; + + // Producer + ServerConfiguration server_configuration; + + int opt; + while ((opt = getopt(argc, argv, "DSCf:b:d:W:c:vs:rmlk:y:p:hi:x")) != -1) { + switch (opt) { + // Common + case 'D': + daemon = true; + break; + case 'f': + log_file = optarg; + break; + + // Server or Client + case 'S': + role -= 1; + break; + case 'C': + role += 1; + break; + + // Client specifc + case 'b': + client_configuration.beta = std::stod(optarg); + options = 1; + break; + case 'd': + client_configuration.drop_factor = std::stod(optarg); + options = 1; + break; + case 'W': + client_configuration.window = std::stod(optarg); + options = 1; + break; + case 'c': + client_configuration.producer_certificate = std::string(optarg); + options = 1; + break; + case 'v': + client_configuration.verify = true; + options = 1; + break; + case 'i': + client_configuration.report_interval_milliseconds_ = std::stoul(optarg); + options = 1; + break; + case 'R': + client_configuration.rtc_ = true; + break; + + // Server specific + case 's': + server_configuration.download_size = std::stoul(optarg); + options = -1; + break; + case 'r': + server_configuration.virtual_producer = false; + options = -1; + break; + case 'm': + server_configuration.manifest = true; + options = -1; + break; + case 'l': + server_configuration.live_production = true; + options = -1; + break; + case 'k': + server_configuration.keystore_name = std::string(optarg); + server_configuration.sign = true; + options = -1; + break; + case 'y': + if (strncasecmp(optarg, "sha256", 6) == 0) { + server_configuration.hash_algorithm = HashAlgorithm::SHA_256; + } else if (strncasecmp(optarg, "sha512", 6) == 0) { + server_configuration.hash_algorithm = HashAlgorithm::SHA_512; + } else if (strncasecmp(optarg, "crc32", 5) == 0) { + server_configuration.hash_algorithm = HashAlgorithm::CRC32C; + } else { + std::cerr << "Ignored unknown hash algorithm. Using SHA 256." + << std::endl; + } + options = -1; + break; + case 'p': + server_configuration.keystore_password = std::string(optarg); + options = -1; + break; + case 'x': + server_configuration.multiphase_produce_ = true; + options = -1; + break; + case 'h': + default: + usage(); + return EXIT_FAILURE; + } + } + + if (options > 0 && role < 0) { + std::cerr << "Client options cannot be used when using the " + "software in server mode" + << std::endl; + usage(); + return EXIT_FAILURE; + + } else if (options < 0 && role > 0) { + std::cerr << "Server options cannot be used when using the " + "software in client mode" + << std::endl; + usage(); + return EXIT_FAILURE; + } else if (!role) { + std::cerr << "Please specify if running hiperf as client " + "or server." + << std::endl; + usage(); + return EXIT_FAILURE; + } + + if (argv[optind] == 0) { + std::cerr << "Please specify the name/prefix to use." << std::endl; + usage(); + return EXIT_FAILURE; + } else { + if (role > 0) { + client_configuration.name = Name(argv[optind]); + } else { + server_configuration.name = Prefix(argv[optind]); + } + } + + if (log_file) { + int fd = open(log_file, O_WRONLY | O_APPEND | O_CREAT, S_IWUSR | S_IRUSR); + dup2(fd, STDOUT_FILENO); + dup2(STDOUT_FILENO, STDERR_FILENO); + close(fd); + } + + if (daemon) { + utils::Daemonizator::daemonize(false); + } + + if (role > 0) { + HIperfClient c(client_configuration); + if (c.setup() != ERROR_SETUP) { + c.run(); + } + } else if (role < 0) { + HIperfServer s(server_configuration); + if (s.setup() != ERROR_SETUP) { + s.run(); + } + } else { + usage(); + return EXIT_FAILURE; + } + + std::cout << "Bye bye" << std::endl; + + return 0; +} + +} // end namespace interface + +} // end namespace transport + +int main(int argc, char *argv[]) { + return transport::interface::main(argc, argv); +} diff --git a/utils/src/ping_client.cc b/utils/src/ping_client.cc new file mode 100755 index 000000000..178bd8bac --- /dev/null +++ b/utils/src/ping_client.cc @@ -0,0 +1,428 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include + +#define SYN_STATE 1 +#define ACK_STATE 2 + +namespace transport { + +namespace core { + +namespace ping { + +typedef std::map SendTimeMap; +typedef utils::Verifier Verifier; + +class Configuration { + public: + uint64_t interestLifetime_; + uint64_t pingInterval_; + uint64_t maxPing_; + uint64_t first_suffix_; + std::string name_; + std::string certificate_; + uint16_t srcPort_; + uint16_t dstPort_; + bool verbose_; + bool dump_; + bool jump_; + bool open_; + bool always_syn_; + bool always_ack_; + bool quiet_; + uint32_t jump_freq_; + uint32_t jump_size_; + uint8_t ttl_; + + Configuration() { + interestLifetime_ = 500; // ms + pingInterval_ = 1000000; // us + maxPing_ = 10; // number of interests + first_suffix_ = 0; + name_ = "b001::1"; // string + srcPort_ = 9695; + dstPort_ = 8080; + verbose_ = false; + dump_ = false; + jump_ = false; + open_ = false; + always_syn_ = false; + always_ack_ = false; + quiet_ = false; + jump_freq_ = 0; + jump_size_ = 0; + ttl_ = 64; + } +}; + +class Client : interface::BasePortal::ConsumerCallback { + public: + Client(Configuration *c) : portal_() { + // Let the main thread to catch SIGINT and SIGQUIT + // asio::signal_set signals(io_service, SIGINT, SIGQUIT); + // signals.async_wait(std::bind(&Client::afterSignal, this)); + + portal_.connect(); + portal_.setConsumerCallback(this); + timer_.reset(new asio::steady_timer(portal_.getIoService())); + config_ = c; + sequence_number_ = config_->first_suffix_; + last_jump_ = 0; + processed_ = 0; + state_ = SYN_STATE; + sent_ = 0; + received_ = 0; + timedout_ = 0; + if (!c->certificate_.empty()) { + key_id_ = verifier_.addKeyFromCertificate(c->certificate_); + } + } + + void ping() { + std::cout << "start ping" << std::endl; + doPing(); + portal_.runEventsLoop(); + } + + void onContentObject(Interest::Ptr &&interest, + ContentObject::Ptr &&object) override { + uint64_t rtt = 0; + + if (!config_->certificate_.empty()) { + auto t0 = std::chrono::steady_clock::now(); + if (verifier_.verify(*object)) { + auto t1 = std::chrono::steady_clock::now(); + auto dt = + std::chrono::duration_cast(t1 - t0); + std::cout << "Verification time: " << dt.count() << std::endl; + std::cout << "<<<<<< Signature OK!!!" << std::endl; + } else { + std::cout << "<<<<<< Signature verification failed!" << std::endl; + } + } + + auto it = send_timestamps_.find(interest->getName().getSuffix()); + if (it != send_timestamps_.end()) { + rtt = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count() - + it->second; + send_timestamps_.erase(it); + } + + if (config_->verbose_) { + std::cout << "<<< recevied object. " << std::endl; + std::cout << "<<< interest name: " << interest->getName() + << " src port: " << interest->getSrcPort() + << " dst port: " << interest->getDstPort() + << " flags: " << interest->printFlags() << std::endl; + std::cout << "<<< object name: " << object->getName() + << " src port: " << object->getSrcPort() + << " dst port: " << object->getDstPort() + << " flags: " << object->printFlags() << " path label " + << object->getPathLabel() << " (" + << (object->getPathLabel() >> 24) << ")" + << " TTL: " << (int)object->getTTL() << std::endl; + } else if (!config_->quiet_) { + std::cout << "<<< received object. " << std::endl; + std::cout << "<<< round trip: " << rtt << " [us]" << std::endl; + std::cout << "<<< interest name: " << interest->getName() << std::endl; + std::cout << "<<< object name: " << object->getName() << std::endl; + std::cout << "<<< content object size: " + << object->payloadSize() + object->headerSize() << " [bytes]" + << std::endl; + } + + if (config_->dump_) { + std::cout << "----- interest dump -----" << std::endl; + interest->dump(); + std::cout << "-------------------------" << std::endl; + std::cout << "----- object dump -------" << std::endl; + object->dump(); + std::cout << "-------------------------" << std::endl; + } + + if (!config_->quiet_) std::cout << std::endl; + + if (!config_->always_syn_) { + if (object->testSyn() && object->testAck() && state_ == SYN_STATE) { + state_ = ACK_STATE; + } + } + + received_++; + processed_++; + if (processed_ >= config_->maxPing_) { + afterSignal(); + } + } + + void onTimeout(Interest::Ptr &&interest) override { + if (config_->verbose_) { + std::cout << "### timeout for " << interest->getName() + << " src port: " << interest->getSrcPort() + << " dst port: " << interest->getDstPort() + << " flags: " << interest->printFlags() << std::endl; + } else if (!config_->quiet_) { + std::cout << "### timeout for " << interest->getName() << std::endl; + } + + if (config_->dump_) { + std::cout << "----- interest dump -----" << std::endl; + interest->dump(); + std::cout << "-------------------------" << std::endl; + } + + if (!config_->quiet_) std::cout << std::endl; + + timedout_++; + processed_++; + if (processed_ >= config_->maxPing_) { + afterSignal(); + } + } + + void doPing() { + Name interest_name(config_->name_, sequence_number_); + hicn_format_t format; + if (interest_name.getAddressFamily() == AF_INET) { + format = HF_INET_TCP; + } else { + format = HF_INET6_TCP; + } + + Interest::Ptr interest(new Interest(std::move(interest_name), format), + nullptr); + + interest->setLifetime(uint32_t(config_->interestLifetime_)); + + interest->resetFlags(); + + if (config_->open_ || config_->always_syn_) { + if (state_ == SYN_STATE) { + interest->setSyn(); + } else if (state_ == ACK_STATE) { + interest->setAck(); + } + } else if (config_->always_ack_) { + interest->setAck(); + } + + interest->setSrcPort(config_->srcPort_); + interest->setDstPort(config_->dstPort_); + interest->setTTL(config_->ttl_); + + if (config_->verbose_) { + std::cout << ">>> send interest " << interest->getName() + << " src port: " << interest->getSrcPort() + << " dst port: " << interest->getDstPort() + << " flags: " << interest->printFlags() + << " TTL: " << (int)interest->getTTL() << std::endl; + } else if (!config_->quiet_) { + std::cout << ">>> send interest " << interest->getName() << std::endl; + } + + if (config_->dump_) { + std::cout << "----- interest dump -----" << std::endl; + interest->dump(); + std::cout << "-------------------------" << std::endl; + } + + if (!config_->quiet_) std::cout << std::endl; + + send_timestamps_[sequence_number_] = + std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + + portal_.sendInterest(std::move(interest)); + + sequence_number_++; + sent_++; + + if (sent_ < config_->maxPing_) { + this->timer_->expires_from_now( + std::chrono::microseconds(config_->pingInterval_)); + this->timer_->async_wait([this](const std::error_code e) { doPing(); }); + } + } + + void afterSignal() { + std::cout << "Stop ping" << std::endl; + std::cout << "Sent: " << sent_ << " Received: " << received_ + << " Timeouts: " << timedout_ << std::endl; + portal_.stopEventsLoop(); + } + + void reset() { + timer_.reset(new asio::steady_timer(portal_.getIoService())); + sequence_number_ = config_->first_suffix_; + last_jump_ = 0; + processed_ = 0; + state_ = SYN_STATE; + sent_ = 0; + received_ = 0; + timedout_ = 0; + } + + private: + SendTimeMap send_timestamps_; + interface::BasePortal portal_; + uint64_t sequence_number_; + uint64_t last_jump_; + uint64_t processed_; + uint32_t state_; + uint32_t sent_; + uint32_t received_; + uint32_t timedout_; + std::unique_ptr timer_; + Configuration *config_; + Verifier verifier_; + PARCKeyId *key_id_; +}; + +void help() { + std::cout << "usage: hicn-consumer-ping [options]" << std::endl; + std::cout << "PING options" << std::endl; + std::cout + << "-i ping interval in microseconds (default 1000000ms)" + << std::endl; + std::cout << "-m maximum number of pings to send (default 10)" + << std::endl; + std::cout << "-s sorce port (default 9695)" << std::endl; + std::cout << "-d destination port (default 8080)" << std::endl; + std::cout << "-t set packet ttl (default 64)" << std::endl; + std::cout << "-O open tcp connection (three way handshake) " + "(default false)" + << std::endl; + std::cout << "-S send always syn messages (default false)" + << std::endl; + std::cout << "-A send always ack messages (default false)" + << std::endl; + std::cout << "HICN options" << std::endl; + std::cout << "-n hicn name (default b001::1)" << std::endl; + std::cout + << "-l interest lifetime in milliseconds (default 500ms)" + << std::endl; + std::cout << "OUTPUT options" << std::endl; + std::cout << "-V verbose, prints statistics about the " + "messagges sent and received (default false)" + << std::endl; + std::cout << "-D dump, dumps sent and received packets " + "(default false)" + << std::endl; + std::cout << "-q quiet, not prints (default false)" + << std::endl; + std::cout << "-H prints this message" << std::endl; +} + +int main(int argc, char *argv[]) { + Configuration *c = new Configuration(); + int opt; + std::string producer_certificate = ""; + + while ((opt = getopt(argc, argv, "j::t:i:m:s:d:n:l:f:c:SAOqVDH")) != -1) { + switch (opt) { + case 't': + c->ttl_ = (uint8_t)std::stoi(optarg); + break; + case 'i': + c->pingInterval_ = std::stoi(optarg); + break; + case 'm': + c->maxPing_ = std::stoi(optarg); + break; + case 'f': + c->first_suffix_ = std::stoul(optarg); + break; + case 's': + c->srcPort_ = std::stoi(optarg); + break; + case 'd': + c->dstPort_ = std::stoi(optarg); + break; + case 'n': + c->name_ = optarg; + break; + case 'l': + c->interestLifetime_ = std::stoi(optarg); + break; + case 'V': + c->verbose_ = true; + ; + break; + case 'D': + c->dump_ = true; + break; + case 'O': + c->always_syn_ = false; + c->always_ack_ = false; + c->open_ = true; + break; + case 'S': + c->always_syn_ = true; + c->always_ack_ = false; + c->open_ = false; + break; + case 'A': + c->always_syn_ = false; + c->always_ack_ = true; + c->open_ = false; + break; + case 'q': + c->quiet_ = true; + c->verbose_ = false; + c->dump_ = false; + break; + case 'c': + c->certificate_ = std::string(optarg); + break; + case 'H': + default: + help(); + exit(EXIT_FAILURE); + } + } + + Client *ping = new Client(c); + + auto t0 = std::chrono::steady_clock::now(); + ping->ping(); + auto t1 = std::chrono::steady_clock::now(); + + std::cout + << "Elapsed time: " + << std::chrono::duration_cast(t1 - t0).count() + << std::endl; + + return 0; +} + +} // namespace ping + +} // namespace core + +} // namespace transport + +int main(int argc, char *argv[]) { + return transport::core::ping::main(argc, argv); +} diff --git a/utils/src/ping_server.cc b/utils/src/ping_server.cc new file mode 100755 index 000000000..19de34fec --- /dev/null +++ b/utils/src/ping_server.cc @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +namespace transport { + +namespace interface { + +using HashAlgorithm = core::HashAlgorithm; +using CryptoSuite = utils::CryptoSuite; + +utils::Identity setProducerIdentity(std::string keystore_name, + std::string keystore_password, + HashAlgorithm hash_algorithm) { + if (access(keystore_name.c_str(), F_OK) != -1) { + return utils::Identity(keystore_name, keystore_password, hash_algorithm); + } else { + return utils::Identity(keystore_name, keystore_password, + CryptoSuite::RSA_SHA256, 1024, 365, "producer-test"); + } +} + +class CallbackContainer { + const std::size_t log2_content_object_buffer_size = 12; + + public: + CallbackContainer(const Name &prefix, uint32_t object_size, bool verbose, + bool dump, bool quite, bool flags, bool reset, uint8_t ttl, + utils::Identity *identity, bool sign) + : buffer_(object_size, 'X'), + content_objects_(1 << log2_content_object_buffer_size), + mask_((1 << log2_content_object_buffer_size) - 1), + content_objects_index_(0), + verbose_(verbose), + dump_(dump), + quite_(quite), + flags_(flags), + reset_(reset), + ttl_(ttl), + identity_(identity), + sign_(sign) { + core::Packet::Format format; + + if (prefix.getAddressFamily() == AF_INET) { + format = core::Packet::Format::HF_INET_TCP; + if (sign_) { + format = core::Packet::Format::HF_INET_TCP_AH; + } + } else { + format = core::Packet::Format::HF_INET6_TCP; + if (sign_) { + format = core::Packet::Format::HF_INET6_TCP_AH; + } + } + + for (int i = 0; i < (1 << log2_content_object_buffer_size); i++) { + content_objects_[i] = std::make_shared( + prefix, format, (const uint8_t *)buffer_.data(), buffer_.size()); + content_objects_[i]->setLifetime( + default_values::content_object_expiry_time); + } + } + + void processInterest(ProducerSocket &p, const Interest &interest) { + if (verbose_) { + std::cout << "<<< received interest " << interest.getName() + << " src port: " << interest.getSrcPort() + << " dst port: " << interest.getDstPort() + << " flags: " << interest.printFlags() + << "TTL: " << (int)interest.getTTL() << std::endl; + } else if (!quite_) { + std::cout << "<<< received interest " << interest.getName() << std::endl; + } + + if (dump_) { + std::cout << "----- interest dump -----" << std::endl; + interest.dump(); + std::cout << "-------------------------" << std::endl; + } + + if (interest.testRst()) { + std::cout << "!!!got a reset, I don't reply" << std::endl; + } else { + auto &content_object = content_objects_[content_objects_index_++ & mask_]; + + content_object->setName(interest.getName()); + content_object->setLifetime(default_values::content_object_expiry_time); + content_object->setLocator(interest.getLocator()); + content_object->setSrcPort(interest.getDstPort()); + content_object->setDstPort(interest.getSrcPort()); + content_object->setTTL(ttl_); + + if (sign_) { + content_object->setSignatureSize(identity_->getSignatureLength()); + } else { + content_object->resetFlags(); + } + + if (flags_) { + if (interest.testSyn()) { + content_object->setSyn(); + content_object->setAck(); + } else if (interest.testAck()) { + content_object->setAck(); + } // here I may need to handle the FIN flag; + } else if (reset_) { + content_object->setRst(); + } + + if (verbose_) { + std::cout << ">>> send object " << content_object->getName() + << " src port: " << content_object->getSrcPort() + << " dst port: " << content_object->getDstPort() + << " flags: " << content_object->printFlags() + << " TTL: " << (int)content_object->getTTL() << std::endl; + } else if (!quite_) { + std::cout << ">>> send object " << content_object->getName() + << std::endl; + } + + if (dump_) { + std::cout << "----- object dump -----" << std::endl; + content_object->dump(); + std::cout << "-----------------------" << std::endl; + } + + if (!quite_) std::cout << std::endl; + + if (sign_) { + identity_->getSigner().sign(*content_object); + } + + p.produce(*content_object); + } + } + + private: + std::string buffer_; + std::vector> content_objects_; + std::uint16_t mask_; + std::uint16_t content_objects_index_; + bool verbose_; + bool dump_; + bool quite_; + bool flags_; + bool reset_; + uint8_t ttl_; + utils::Identity *identity_; + bool sign_; +}; + +void help() { + std::cout << "usage: hicn-preoducer-ping [options]" << std::endl; + std::cout << "PING options" << std::endl; + std::cout << "-s object content size (default 1350B)" << std::endl; + std::cout << "-n hicn name (default b001::/64)" << std::endl; + std::cout << "-f set tcp flags according to the flag received " + "(default false)" + << std::endl; + std::cout << "-r always reply with a reset flag (default false)" + << std::endl; + std::cout << "-t set ttl (default 64)" << std::endl; + std::cout << "OUTPUT options" << std::endl; + std::cout << "-V verbose, prints statistics about the messagges sent " + "and received (default false)" + << std::endl; + std::cout << "-D dump, dumps sent and received packets (default false)" + << std::endl; + std::cout << "-q quite, not prints (default false)" << std::endl; + std::cout << "-d daemon mode" << std::endl; + std::cout << "-H prints this message" << std::endl; +} + +int main(int argc, char **argv) { + std::string name_prefix = "b001::0/64"; + std::string delimiter = "/"; + bool daemon = false; + bool verbose = false; + bool dump = false; + bool quite = false; + bool flags = false; + bool reset = false; + uint32_t object_size = 1350; + uint8_t ttl = 64; + std::string keystore_path = "./rsa_crypto_material.p12"; + std::string keystore_password = "cisco"; + bool sign = false; + + int opt; + while ((opt = getopt(argc, argv, "s:n:t:qfrVDdHk:p:")) != -1) { + switch (opt) { + case 's': + object_size = std::stoi(optarg); + break; + case 'n': + name_prefix = optarg; + break; + case 't': + ttl = (uint8_t)std::stoi(optarg); + break; + case 'V': + verbose = true; + break; + case 'D': + dump = true; + break; + case 'q': + verbose = false; + dump = false; + quite = true; + break; + case 'd': + daemon = true; + break; + case 'f': + flags = true; + break; + case 'r': + reset = true; + break; + case 'k': + keystore_path = optarg; + sign = true; + break; + case 'p': + keystore_password = optarg; + break; + case 'H': + default: + help(); + exit(EXIT_FAILURE); + } + } + + if (daemon) { + utils::Daemonizator::daemonize(); + } + + core::Prefix producer_namespace(name_prefix); + + utils::StringTokenizer tokenizer(name_prefix, delimiter); + std::string ip_address = tokenizer.nextToken(); + Name n(ip_address); + + if (object_size > 1350) object_size = 1350; + + CallbackContainer *stubs; + utils::Identity identity = setProducerIdentity( + keystore_path, keystore_password, HashAlgorithm::SHA_256); + + if (sign) { + stubs = new CallbackContainer(n, object_size, verbose, dump, quite, flags, + reset, ttl, &identity, sign); + } else { + utils::Identity *identity = nullptr; + stubs = new CallbackContainer(n, object_size, verbose, dump, quite, flags, + reset, ttl, identity, sign); + } + + asio::io_service io_service; + + ProducerSocket p(io_service); // , setProducerIdentity()); + p.registerPrefix(producer_namespace); + + p.setSocketOption(GeneralTransportOptions::OUTPUT_BUFFER_SIZE, 0U); + p.setSocketOption(ProducerCallbacksOptions::CACHE_MISS, + (ProducerInterestCallback)bind( + &CallbackContainer::processInterest, stubs, + std::placeholders::_1, std::placeholders::_2)); + + p.connect(); + + p.serveForever(); + + return 0; +} + +} // namespace interface + +} // end namespace transport + +int main(int argc, char **argv) { + return transport::interface::main(argc, argv); +} -- cgit 1.2.3-korg