summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitreview4
-rwxr-xr-xAUTHORS6
-rwxr-xr-xCMakeLists.txt51
-rwxr-xr-xLICENSE202
-rwxr-xr-xREADME.md33
-rwxr-xr-xapps/README.md2
-rwxr-xr-xcmake/Modules/BuildMacros.cmake146
-rwxr-xr-xcmake/Modules/FindAsio.cmake41
-rwxr-xr-xcmake/Modules/FindGFlags.cmake36
-rwxr-xr-xcmake/Modules/FindGlog.cmake36
-rwxr-xr-xcmake/Modules/FindHicnBinaryApi.cmake31
-rwxr-xr-xcmake/Modules/FindLibEvent.cmake55
-rwxr-xr-xcmake/Modules/FindLibhicn.cmake49
-rwxr-xr-xcmake/Modules/FindLibmemif.cmake47
-rwxr-xr-xcmake/Modules/FindLibparc.cmake50
-rwxr-xr-xcmake/Modules/FindLibtransport.cmake48
-rwxr-xr-xcmake/Modules/FindLongBow.cmake55
-rwxr-xr-xcmake/Modules/FindUncrustify.cmake21
-rwxr-xr-xcmake/Modules/FindVpp.cmake67
-rwxr-xr-xcmake/Modules/IosMacros.cmake24
-rwxr-xr-xcmake/Modules/Packager.cmake105
-rwxr-xr-xcmake/Modules/detectCacheSize.cmake21
-rwxr-xr-xhicn-light/CMakeLists.txt74
-rwxr-xr-xhicn-light/README.md302
-rwxr-xr-xhicn-light/config/hicn-light.service29
-rwxr-xr-xhicn-light/src/CMakeLists.txt45
-rwxr-xr-xhicn-light/src/command_line/CMakeLists.txt2
-rwxr-xr-xhicn-light/src/command_line/controller/CMakeLists.txt23
-rwxr-xr-xhicn-light/src/command_line/controller/hicnLightControl_main.c284
-rwxr-xr-xhicn-light/src/command_line/daemon/CMakeLists.txt23
-rwxr-xr-xhicn-light/src/command_line/daemon/hicnLightDaemon_main.c336
-rwxr-xr-xhicn-light/src/config.h.in5
-rwxr-xr-xhicn-light/src/config/CMakeLists.txt97
-rwxr-xr-xhicn-light/src/config/commandOps.c72
-rwxr-xr-xhicn-light/src/config/commandOps.h126
-rwxr-xr-xhicn-light/src/config/commandParser.c216
-rwxr-xr-xhicn-light/src/config/commandParser.h196
-rwxr-xr-xhicn-light/src/config/commandReturn.h43
-rwxr-xr-xhicn-light/src/config/configuration.c1076
-rwxr-xr-xhicn-light/src/config/configuration.h152
-rwxr-xr-xhicn-light/src/config/configurationFile.c300
-rwxr-xr-xhicn-light/src/config/configurationFile.h93
-rwxr-xr-xhicn-light/src/config/configurationListeners.c535
-rwxr-xr-xhicn-light/src/config/configurationListeners.h62
-rwxr-xr-xhicn-light/src/config/controlAdd.c94
-rwxr-xr-xhicn-light/src/config/controlAdd.h32
-rwxr-xr-xhicn-light/src/config/controlAddConnection.c390
-rwxr-xr-xhicn-light/src/config/controlAddConnection.h31
-rwxr-xr-xhicn-light/src/config/controlAddListener.c178
-rwxr-xr-xhicn-light/src/config/controlAddListener.h30
-rwxr-xr-xhicn-light/src/config/controlAddPunting.c154
-rwxr-xr-xhicn-light/src/config/controlAddPunting.h22
-rwxr-xr-xhicn-light/src/config/controlAddRoute.c157
-rwxr-xr-xhicn-light/src/config/controlAddRoute.h30
-rwxr-xr-xhicn-light/src/config/controlCache.c91
-rwxr-xr-xhicn-light/src/config/controlCache.h22
-rwxr-xr-xhicn-light/src/config/controlCacheClear.c84
-rwxr-xr-xhicn-light/src/config/controlCacheClear.h30
-rwxr-xr-xhicn-light/src/config/controlCacheServe.c103
-rwxr-xr-xhicn-light/src/config/controlCacheServe.h22
-rwxr-xr-xhicn-light/src/config/controlCacheStore.c103
-rwxr-xr-xhicn-light/src/config/controlCacheStore.h22
-rwxr-xr-xhicn-light/src/config/controlList.c97
-rwxr-xr-xhicn-light/src/config/controlList.h30
-rwxr-xr-xhicn-light/src/config/controlListConnections.c160
-rwxr-xr-xhicn-light/src/config/controlListConnections.h30
-rwxr-xr-xhicn-light/src/config/controlListInterfaces.c76
-rwxr-xr-xhicn-light/src/config/controlListInterfaces.h31
-rwxr-xr-xhicn-light/src/config/controlListListeners.c141
-rwxr-xr-xhicn-light/src/config/controlListListeners.h31
-rwxr-xr-xhicn-light/src/config/controlListRoutes.c154
-rwxr-xr-xhicn-light/src/config/controlListRoutes.h29
-rwxr-xr-xhicn-light/src/config/controlMapMe.c94
-rwxr-xr-xhicn-light/src/config/controlMapMe.h22
-rwxr-xr-xhicn-light/src/config/controlMapMeDiscovery.c103
-rwxr-xr-xhicn-light/src/config/controlMapMeDiscovery.h22
-rwxr-xr-xhicn-light/src/config/controlMapMeEnable.c101
-rwxr-xr-xhicn-light/src/config/controlMapMeEnable.h22
-rwxr-xr-xhicn-light/src/config/controlMapMeRetx.c94
-rwxr-xr-xhicn-light/src/config/controlMapMeRetx.h22
-rwxr-xr-xhicn-light/src/config/controlMapMeTimescale.c97
-rwxr-xr-xhicn-light/src/config/controlMapMeTimescale.h22
-rwxr-xr-xhicn-light/src/config/controlQuit.c63
-rwxr-xr-xhicn-light/src/config/controlQuit.h29
-rwxr-xr-xhicn-light/src/config/controlRemove.c92
-rwxr-xr-xhicn-light/src/config/controlRemove.h29
-rwxr-xr-xhicn-light/src/config/controlRemoveConnection.c115
-rwxr-xr-xhicn-light/src/config/controlRemoveConnection.h31
-rwxr-xr-xhicn-light/src/config/controlRemovePunting.c76
-rwxr-xr-xhicn-light/src/config/controlRemovePunting.h27
-rwxr-xr-xhicn-light/src/config/controlRemoveRoute.c150
-rwxr-xr-xhicn-light/src/config/controlRemoveRoute.h31
-rwxr-xr-xhicn-light/src/config/controlRoot.c134
-rwxr-xr-xhicn-light/src/config/controlRoot.h31
-rwxr-xr-xhicn-light/src/config/controlSet.c88
-rwxr-xr-xhicn-light/src/config/controlSet.h29
-rwxr-xr-xhicn-light/src/config/controlSetDebug.c74
-rwxr-xr-xhicn-light/src/config/controlSetDebug.h30
-rwxr-xr-xhicn-light/src/config/controlSetStrategy.c183
-rwxr-xr-xhicn-light/src/config/controlSetStrategy.h22
-rwxr-xr-xhicn-light/src/config/controlSetWldr.c122
-rwxr-xr-xhicn-light/src/config/controlSetWldr.h22
-rwxr-xr-xhicn-light/src/config/controlState.c237
-rwxr-xr-xhicn-light/src/config/controlState.h233
-rwxr-xr-xhicn-light/src/config/controlUnset.c78
-rwxr-xr-xhicn-light/src/config/controlUnset.h29
-rwxr-xr-xhicn-light/src/config/controlUnsetDebug.c77
-rwxr-xr-xhicn-light/src/config/controlUnsetDebug.h30
-rwxr-xr-xhicn-light/src/config/symbolicNameTable.c175
-rwxr-xr-xhicn-light/src/config/symbolicNameTable.h131
-rwxr-xr-xhicn-light/src/content_store/CMakeLists.txt33
-rwxr-xr-xhicn-light/src/content_store/contentStoreEntry.c136
-rwxr-xr-xhicn-light/src/content_store/contentStoreEntry.h146
-rwxr-xr-xhicn-light/src/content_store/contentStoreInterface.c57
-rwxr-xr-xhicn-light/src/content_store/contentStoreInterface.h211
-rwxr-xr-xhicn-light/src/content_store/contentStoreLRU.c455
-rwxr-xr-xhicn-light/src/content_store/contentStoreLRU.h39
-rwxr-xr-xhicn-light/src/content_store/listLRU.c133
-rwxr-xr-xhicn-light/src/content_store/listLRU.h94
-rwxr-xr-xhicn-light/src/content_store/listTimeOrdered.c94
-rwxr-xr-xhicn-light/src/content_store/listTimeOrdered.h103
-rwxr-xr-xhicn-light/src/core/CMakeLists.txt55
-rwxr-xr-xhicn-light/src/core/connection.c265
-rwxr-xr-xhicn-light/src/core/connection.h148
-rwxr-xr-xhicn-light/src/core/connectionList.c68
-rwxr-xr-xhicn-light/src/core/connectionList.h70
-rwxr-xr-xhicn-light/src/core/connectionManager.c196
-rwxr-xr-xhicn-light/src/core/connectionManager.h37
-rwxr-xr-xhicn-light/src/core/connectionTable.c224
-rwxr-xr-xhicn-light/src/core/connectionTable.h99
-rwxr-xr-xhicn-light/src/core/dispatcher.c435
-rwxr-xr-xhicn-light/src/core/dispatcher.h286
-rwxr-xr-xhicn-light/src/core/forwarder.c499
-rwxr-xr-xhicn-light/src/core/forwarder.h287
-rwxr-xr-xhicn-light/src/core/logger.c173
-rwxr-xr-xhicn-light/src/core/logger.h168
-rwxr-xr-xhicn-light/src/core/mapMe.c816
-rwxr-xr-xhicn-light/src/core/mapMe.h89
-rwxr-xr-xhicn-light/src/core/message.c297
-rwxr-xr-xhicn-light/src/core/message.h180
-rwxr-xr-xhicn-light/src/core/messageHandler.h580
-rwxr-xr-xhicn-light/src/core/messagePacketType.h32
-rwxr-xr-xhicn-light/src/core/name.c236
-rwxr-xr-xhicn-light/src/core/name.h105
-rwxr-xr-xhicn-light/src/core/nameBitvector.c383
-rwxr-xr-xhicn-light/src/core/nameBitvector.h63
-rwxr-xr-xhicn-light/src/core/numberSet.c203
-rwxr-xr-xhicn-light/src/core/numberSet.h157
-rwxr-xr-xhicn-light/src/core/streamBuffer.c142
-rwxr-xr-xhicn-light/src/core/streamBuffer.h129
-rwxr-xr-xhicn-light/src/core/system.h60
-rwxr-xr-xhicn-light/src/core/ticks.h31
-rwxr-xr-xhicn-light/src/core/wldr.c182
-rwxr-xr-xhicn-light/src/core/wldr.h52
-rwxr-xr-xhicn-light/src/io/CMakeLists.txt53
-rwxr-xr-xhicn-light/src/io/addressPair.c129
-rwxr-xr-xhicn-light/src/io/addressPair.h128
-rwxr-xr-xhicn-light/src/io/hicnConnection.c517
-rwxr-xr-xhicn-light/src/io/hicnConnection.h53
-rwxr-xr-xhicn-light/src/io/hicnListener.c725
-rwxr-xr-xhicn-light/src/io/hicnListener.h42
-rwxr-xr-xhicn-light/src/io/hicnTunnel.c106
-rwxr-xr-xhicn-light/src/io/hicnTunnel.h65
-rwxr-xr-xhicn-light/src/io/ioOperations.c63
-rwxr-xr-xhicn-light/src/io/ioOperations.h394
-rwxr-xr-xhicn-light/src/io/listener.h105
-rwxr-xr-xhicn-light/src/io/listenerSet.c132
-rwxr-xr-xhicn-light/src/io/listenerSet.h137
-rwxr-xr-xhicn-light/src/io/streamConnection.c714
-rwxr-xr-xhicn-light/src/io/streamConnection.h75
-rwxr-xr-xhicn-light/src/io/tcpListener.c233
-rwxr-xr-xhicn-light/src/io/tcpListener.h37
-rwxr-xr-xhicn-light/src/io/tcpTunnel.c43
-rwxr-xr-xhicn-light/src/io/tcpTunnel.h42
-rwxr-xr-xhicn-light/src/io/udpConnection.c406
-rwxr-xr-xhicn-light/src/io/udpConnection.h53
-rwxr-xr-xhicn-light/src/io/udpListener.c533
-rwxr-xr-xhicn-light/src/io/udpListener.h32
-rwxr-xr-xhicn-light/src/io/udpTunnel.c91
-rwxr-xr-xhicn-light/src/io/udpTunnel.h42
-rwxr-xr-xhicn-light/src/messenger/CMakeLists.txt32
-rwxr-xr-xhicn-light/src/messenger/messenger.c171
-rwxr-xr-xhicn-light/src/messenger/messenger.h69
-rwxr-xr-xhicn-light/src/messenger/messengerRecipient.c62
-rwxr-xr-xhicn-light/src/messenger/messengerRecipient.h104
-rwxr-xr-xhicn-light/src/messenger/missive.c54
-rwxr-xr-xhicn-light/src/messenger/missive.h89
-rwxr-xr-xhicn-light/src/messenger/missiveDeque.c77
-rwxr-xr-xhicn-light/src/messenger/missiveDeque.h55
-rwxr-xr-xhicn-light/src/messenger/missiveType.h55
-rwxr-xr-xhicn-light/src/platforms/CMakeLists.txt31
-rwxr-xr-xhicn-light/src/platforms/README.txt17
-rwxr-xr-xhicn-light/src/platforms/android/system.c183
-rwxr-xr-xhicn-light/src/platforms/darwin/system.c146
-rwxr-xr-xhicn-light/src/platforms/linux/system.c184
-rwxr-xr-xhicn-light/src/processor/CMakeLists.txt40
-rwxr-xr-xhicn-light/src/processor/fib.c448
-rwxr-xr-xhicn-light/src/processor/fib.h43
-rwxr-xr-xhicn-light/src/processor/fibEntry.c223
-rwxr-xr-xhicn-light/src/processor/fibEntry.h143
-rwxr-xr-xhicn-light/src/processor/fibEntryList.c72
-rwxr-xr-xhicn-light/src/processor/fibEntryList.h96
-rwxr-xr-xhicn-light/src/processor/hashTableFunction.c47
-rwxr-xr-xhicn-light/src/processor/hashTableFunction.h73
-rwxr-xr-xhicn-light/src/processor/matchingRulesTable.c132
-rwxr-xr-xhicn-light/src/processor/matchingRulesTable.h113
-rwxr-xr-xhicn-light/src/processor/messageProcessor.c742
-rwxr-xr-xhicn-light/src/processor/messageProcessor.h166
-rwxr-xr-xhicn-light/src/processor/pit.c45
-rwxr-xr-xhicn-light/src/processor/pit.h114
-rwxr-xr-xhicn-light/src/processor/pitEntry.c142
-rwxr-xr-xhicn-light/src/processor/pitEntry.h164
-rwxr-xr-xhicn-light/src/processor/pitStandard.c304
-rwxr-xr-xhicn-light/src/processor/pitStandard.h41
-rwxr-xr-xhicn-light/src/processor/pitVerdict.h36
-rwxr-xr-xhicn-light/src/socket/CMakeLists.txt31
-rwxr-xr-xhicn-light/src/socket/api.c604
-rwxr-xr-xhicn-light/src/socket/api.h216
-rwxr-xr-xhicn-light/src/socket/error.c7
-rwxr-xr-xhicn-light/src/socket/error.h46
-rwxr-xr-xhicn-light/src/socket/ops.h54
-rwxr-xr-xhicn-light/src/socket/ops_linux.c1723
-rwxr-xr-xhicn-light/src/strategies/CMakeLists.txt36
-rwxr-xr-xhicn-light/src/strategies/loadBalancer.c278
-rwxr-xr-xhicn-light/src/strategies/loadBalancer.h26
-rwxr-xr-xhicn-light/src/strategies/loadBalancerWithPD.c368
-rwxr-xr-xhicn-light/src/strategies/loadBalancerWithPD.h30
-rwxr-xr-xhicn-light/src/strategies/nexthopState.c206
-rwxr-xr-xhicn-light/src/strategies/nexthopState.h94
-rwxr-xr-xhicn-light/src/strategies/nexthopStateWithPD.c254
-rwxr-xr-xhicn-light/src/strategies/nexthopStateWithPD.h106
-rwxr-xr-xhicn-light/src/strategies/rnd.c175
-rwxr-xr-xhicn-light/src/strategies/rnd.h26
-rwxr-xr-xhicn-light/src/strategies/rndSegment.c207
-rwxr-xr-xhicn-light/src/strategies/rndSegment.h27
-rwxr-xr-xhicn-light/src/strategies/strategyImpl.h66
-rwxr-xr-xhicn-light/src/utils/CMakeLists.txt36
-rwxr-xr-xhicn-light/src/utils/address.c419
-rwxr-xr-xhicn-light/src/utils/address.h498
-rwxr-xr-xhicn-light/src/utils/addressList.c133
-rwxr-xr-xhicn-light/src/utils/addressList.h196
-rwxr-xr-xhicn-light/src/utils/commands.h282
-rwxr-xr-xhicn-light/src/utils/interface.c168
-rwxr-xr-xhicn-light/src/utils/interface.h208
-rwxr-xr-xhicn-light/src/utils/interfaceSet.c149
-rwxr-xr-xhicn-light/src/utils/interfaceSet.h198
-rwxr-xr-xhicn-light/src/utils/punting.c98
-rwxr-xr-xhicn-light/src/utils/punting.h72
-rwxr-xr-xhicn-light/src/utils/utils.c258
-rwxr-xr-xhicn-light/src/utils/utils.h82
-rwxr-xr-xhicn-plugin/AUTHORS4
-rwxr-xr-xhicn-plugin/CMakeLists.txt235
-rwxr-xr-xhicn-plugin/README.md168
-rwxr-xr-xhicn-plugin/src/cache_policies/cs_lru.c213
-rwxr-xr-xhicn-plugin/src/cache_policies/cs_lru.h67
-rwxr-xr-xhicn-plugin/src/cache_policies/cs_policy.h81
-rwxr-xr-xhicn-plugin/src/cli.c1247
-rwxr-xr-xhicn-plugin/src/data_fwd.h209
-rwxr-xr-xhicn-plugin/src/data_fwd_node.c541
-rwxr-xr-xhicn-plugin/src/data_pcslookup.h55
-rwxr-xr-xhicn-plugin/src/data_pcslookup_node.c246
-rwxr-xr-xhicn-plugin/src/data_push_node.c349
-rwxr-xr-xhicn-plugin/src/error.c28
-rwxr-xr-xhicn-plugin/src/error.h100
-rwxr-xr-xhicn-plugin/src/face_db.h153
-rwxr-xr-xhicn-plugin/src/faces/app/address_mgr.c243
-rwxr-xr-xhicn-plugin/src/faces/app/address_mgr.h76
-rwxr-xr-xhicn-plugin/src/faces/app/face_app_cli.c203
-rwxr-xr-xhicn-plugin/src/faces/app/face_cons.c126
-rwxr-xr-xhicn-plugin/src/faces/app/face_cons.h75
-rwxr-xr-xhicn-plugin/src/faces/app/face_prod.c375
-rwxr-xr-xhicn-plugin/src/faces/app/face_prod.h113
-rwxr-xr-xhicn-plugin/src/faces/app/face_prod_node.c341
-rwxr-xr-xhicn-plugin/src/faces/face.c141
-rwxr-xr-xhicn-plugin/src/faces/face.h240
-rwxr-xr-xhicn-plugin/src/faces/face_cli.c131
-rwxr-xr-xhicn-plugin/src/faces/ip/dpo_ip.c187
-rwxr-xr-xhicn-plugin/src/faces/ip/dpo_ip.h255
-rwxr-xr-xhicn-plugin/src/faces/ip/face_ip.c326
-rwxr-xr-xhicn-plugin/src/faces/ip/face_ip.h241
-rwxr-xr-xhicn-plugin/src/faces/ip/face_ip_cli.c158
-rwxr-xr-xhicn-plugin/src/faces/ip/face_ip_node.c761
-rwxr-xr-xhicn-plugin/src/faces/ip/face_ip_node.h40
-rwxr-xr-xhicn-plugin/src/faces/ip/iface_ip_node.c845
-rwxr-xr-xhicn-plugin/src/faces/ip/iface_ip_node.h35
-rwxr-xr-xhicn-plugin/src/faces/udp/dpo_udp.c158
-rwxr-xr-xhicn-plugin/src/faces/udp/dpo_udp.h312
-rwxr-xr-xhicn-plugin/src/faces/udp/face_udp.c371
-rwxr-xr-xhicn-plugin/src/faces/udp/face_udp.h248
-rwxr-xr-xhicn-plugin/src/faces/udp/face_udp_cli.c164
-rwxr-xr-xhicn-plugin/src/faces/udp/face_udp_node.c864
-rwxr-xr-xhicn-plugin/src/faces/udp/face_udp_node.h35
-rwxr-xr-xhicn-plugin/src/faces/udp/iface_udp_node.c894
-rwxr-xr-xhicn-plugin/src/faces/udp/iface_udp_node.h36
-rwxr-xr-xhicn-plugin/src/hashtb.c1008
-rwxr-xr-xhicn-plugin/src/hashtb.h550
-rwxr-xr-xhicn-plugin/src/hicn.api538
-rwxr-xr-xhicn-plugin/src/hicn.c253
-rwxr-xr-xhicn-plugin/src/hicn.h86
-rwxr-xr-xhicn-plugin/src/hicn_all_api_h.h22
-rwxr-xr-xhicn-plugin/src/hicn_api.c570
-rwxr-xr-xhicn-plugin/src/hicn_api.h32
-rwxr-xr-xhicn-plugin/src/hicn_api_test.c1046
-rwxr-xr-xhicn-plugin/src/hicn_msg_enum.h36
-rwxr-xr-xhicn-plugin/src/infra.h101
-rwxr-xr-xhicn-plugin/src/interest_hitcs.h55
-rwxr-xr-xhicn-plugin/src/interest_hitcs_node.c300
-rwxr-xr-xhicn-plugin/src/interest_hitpit.h56
-rwxr-xr-xhicn-plugin/src/interest_hitpit_node.c313
-rwxr-xr-xhicn-plugin/src/interest_pcslookup.h57
-rwxr-xr-xhicn-plugin/src/interest_pcslookup_node.c240
-rwxr-xr-xhicn-plugin/src/mapme.h307
-rwxr-xr-xhicn-plugin/src/mapme_ack.h53
-rwxr-xr-xhicn-plugin/src/mapme_ack_node.c224
-rwxr-xr-xhicn-plugin/src/mapme_ctrl.h92
-rwxr-xr-xhicn-plugin/src/mapme_ctrl_node.c333
-rwxr-xr-xhicn-plugin/src/mapme_eventmgr.c559
-rwxr-xr-xhicn-plugin/src/mapme_eventmgr.h48
-rwxr-xr-xhicn-plugin/src/mgmt.c100
-rwxr-xr-xhicn-plugin/src/mgmt.h132
-rwxr-xr-xhicn-plugin/src/params.h104
-rwxr-xr-xhicn-plugin/src/parser.h102
-rwxr-xr-xhicn-plugin/src/pcs.c53
-rwxr-xr-xhicn-plugin/src/pcs.h836
-rwxr-xr-xhicn-plugin/src/pg.c1147
-rwxr-xr-xhicn-plugin/src/pg.h56
-rwxr-xr-xhicn-plugin/src/punt.c1005
-rwxr-xr-xhicn-plugin/src/punt.h338
-rwxr-xr-xhicn-plugin/src/route.c392
-rwxr-xr-xhicn-plugin/src/route.h61
-rwxr-xr-xhicn-plugin/src/state.h102
-rwxr-xr-xhicn-plugin/src/strategies/dpo_mw.c305
-rwxr-xr-xhicn-plugin/src/strategies/dpo_mw.h131
-rwxr-xr-xhicn-plugin/src/strategies/strategy_mw.c171
-rwxr-xr-xhicn-plugin/src/strategies/strategy_mw.h31
-rwxr-xr-xhicn-plugin/src/strategies/strategy_mw_cli.c148
-rwxr-xr-xhicn-plugin/src/strategy.c265
-rwxr-xr-xhicn-plugin/src/strategy.h82
-rwxr-xr-xhicn-plugin/src/strategy_dpo_ctx.h69
-rwxr-xr-xhicn-plugin/src/strategy_dpo_manager.c159
-rwxr-xr-xhicn-plugin/src/strategy_dpo_manager.h186
-rwxr-xr-xhicn-plugin/src/utils.h66
-rwxr-xr-xhicn-plugin/src/vface_db.h155
-rwxr-xr-xlib/CMakeLists.txt50
-rwxr-xr-xlib/README.md114
-rwxr-xr-xlib/doc/CMakeLists.txt23
-rwxr-xr-xlib/doc/Doxyfile.in12
-rwxr-xr-xlib/src/CMakeLists.txt81
-rwxr-xr-xlib/src/base.h136
-rwxr-xr-xlib/src/common.c165
-rwxr-xr-xlib/src/common.h231
-rwxr-xr-xlib/src/compat.c1177
-rwxr-xr-xlib/src/compat.h462
-rwxr-xr-xlib/src/error.c35
-rwxr-xr-xlib/src/error.h58
-rwxr-xr-xlib/src/header.h105
-rwxr-xr-xlib/src/hicn.h79
-rwxr-xr-xlib/src/mapme.c224
-rwxr-xr-xlib/src/mapme.h152
-rwxr-xr-xlib/src/name.c676
-rwxr-xr-xlib/src/name.h336
-rwxr-xr-xlib/src/ops.c93
-rwxr-xr-xlib/src/ops.h624
-rwxr-xr-xlib/src/protocol.h51
-rwxr-xr-xlib/src/protocol/ah.c211
-rwxr-xr-xlib/src/protocol/ah.h72
-rwxr-xr-xlib/src/protocol/icmp.c229
-rwxr-xr-xlib/src/protocol/icmp.h68
-rwxr-xr-xlib/src/protocol/icmprd.h47
-rwxr-xr-xlib/src/protocol/ipv4.c452
-rwxr-xr-xlib/src/protocol/ipv4.h91
-rwxr-xr-xlib/src/protocol/ipv6.c412
-rwxr-xr-xlib/src/protocol/ipv6.h67
-rwxr-xr-xlib/src/protocol/tcp.c370
-rwxr-xr-xlib/src/protocol/tcp.h166
-rwxr-xr-xlib/src/protocol/udp.h37
-rwxr-xr-xlibtransport/AUTHORS8
-rwxr-xr-xlibtransport/CMakeLists.txt122
-rwxr-xr-xlibtransport/README.md126
-rwxr-xr-xlibtransport/cmake/Modules/Android.cmake19
-rwxr-xr-xlibtransport/cmake/Modules/DefaultConfiguration.cmake48
-rwxr-xr-xlibtransport/cmake/Modules/Ios.cmake23
-rwxr-xr-xlibtransport/cmake/Modules/Packager.cmake197
-rwxr-xr-xlibtransport/cmake/Modules/TestMacros.cmake15
-rwxr-xr-xlibtransport/src/hicn/transport/CMakeLists.txt54
-rwxr-xr-xlibtransport/src/hicn/transport/config.h.in22
-rwxr-xr-xlibtransport/src/hicn/transport/core/CMakeLists.txt87
-rwxr-xr-xlibtransport/src/hicn/transport/core/connector.cc44
-rwxr-xr-xlibtransport/src/hicn/transport/core/connector.h89
-rwxr-xr-xlibtransport/src/hicn/transport/core/content_object.cc170
-rwxr-xr-xlibtransport/src/hicn/transport/core/content_object.h69
-rwxr-xr-xlibtransport/src/hicn/transport/core/facade.h53
-rwxr-xr-xlibtransport/src/hicn/transport/core/forwarder_interface.h136
-rwxr-xr-xlibtransport/src/hicn/transport/core/hicn_binary_api.c228
-rwxr-xr-xlibtransport/src/hicn/transport/core/hicn_binary_api.h103
-rwxr-xr-xlibtransport/src/hicn/transport/core/hicn_forwarder_interface.cc84
-rwxr-xr-xlibtransport/src/hicn/transport/core/hicn_forwarder_interface.h67
-rwxr-xr-xlibtransport/src/hicn/transport/core/hicn_memif_api.c0
-rwxr-xr-xlibtransport/src/hicn/transport/core/interest.cc149
-rwxr-xr-xlibtransport/src/hicn/transport/core/interest.h66
-rwxr-xr-xlibtransport/src/hicn/transport/core/key_locator.cc42
-rwxr-xr-xlibtransport/src/hicn/transport/core/key_locator.h48
-rwxr-xr-xlibtransport/src/hicn/transport/core/key_locator_type.h28
-rwxr-xr-xlibtransport/src/hicn/transport/core/manifest.cc33
-rwxr-xr-xlibtransport/src/hicn/transport/core/manifest.h164
-rwxr-xr-xlibtransport/src/hicn/transport/core/manifest_format.h196
-rwxr-xr-xlibtransport/src/hicn/transport/core/manifest_format_fixed.cc221
-rwxr-xr-xlibtransport/src/hicn/transport/core/manifest_format_fixed.h168
-rwxr-xr-xlibtransport/src/hicn/transport/core/manifest_format_json_jsoncpp.cc244
-rwxr-xr-xlibtransport/src/hicn/transport/core/manifest_format_json_jsoncpp.h162
-rwxr-xr-xlibtransport/src/hicn/transport/core/manifest_format_json_libparc_deprecated.cc298
-rwxr-xr-xlibtransport/src/hicn/transport/core/manifest_format_json_libparc_deprecated.h152
-rwxr-xr-xlibtransport/src/hicn/transport/core/manifest_inline.h116
-rwxr-xr-xlibtransport/src/hicn/transport/core/memif_binary_api.c218
-rwxr-xr-xlibtransport/src/hicn/transport/core/memif_binary_api.h61
-rwxr-xr-xlibtransport/src/hicn/transport/core/memif_connector.cc493
-rwxr-xr-xlibtransport/src/hicn/transport/core/memif_connector.h162
-rwxr-xr-xlibtransport/src/hicn/transport/core/name.cc236
-rwxr-xr-xlibtransport/src/hicn/transport/core/name.h133
-rwxr-xr-xlibtransport/src/hicn/transport/core/packet.cc614
-rwxr-xr-xlibtransport/src/hicn/transport/core/packet.h195
-rwxr-xr-xlibtransport/src/hicn/transport/core/payload_type.h29
-rwxr-xr-xlibtransport/src/hicn/transport/core/pending_interest.cc49
-rwxr-xr-xlibtransport/src/hicn/transport/core/pending_interest.h81
-rwxr-xr-xlibtransport/src/hicn/transport/core/portal.h343
-rwxr-xr-xlibtransport/src/hicn/transport/core/prefix.cc211
-rwxr-xr-xlibtransport/src/hicn/transport/core/prefix.h68
-rwxr-xr-xlibtransport/src/hicn/transport/core/raw_socket_connector.cc214
-rwxr-xr-xlibtransport/src/hicn/transport/core/raw_socket_connector.h85
-rwxr-xr-xlibtransport/src/hicn/transport/core/raw_socket_interface.cc57
-rwxr-xr-xlibtransport/src/hicn/transport/core/raw_socket_interface.h51
-rwxr-xr-xlibtransport/src/hicn/transport/core/socket_connector.cc237
-rwxr-xr-xlibtransport/src/hicn/transport/core/socket_connector.h89
-rwxr-xr-xlibtransport/src/hicn/transport/core/test/CMakeLists.txt10
-rwxr-xr-xlibtransport/src/hicn/transport/core/test/test_core_manifest.cc296
-rwxr-xr-xlibtransport/src/hicn/transport/core/vpp_binary_api.c221
-rwxr-xr-xlibtransport/src/hicn/transport/core/vpp_binary_api.h64
-rwxr-xr-xlibtransport/src/hicn/transport/core/vpp_binary_api_internal.h61
-rwxr-xr-xlibtransport/src/hicn/transport/core/vpp_forwarder_interface.cc184
-rwxr-xr-xlibtransport/src/hicn/transport/core/vpp_forwarder_interface.h68
-rwxr-xr-xlibtransport/src/hicn/transport/errors/CMakeLists.txt29
-rwxr-xr-xlibtransport/src/hicn/transport/errors/errors.h24
-rwxr-xr-xlibtransport/src/hicn/transport/errors/invalid_ip_address_exception.h31
-rwxr-xr-xlibtransport/src/hicn/transport/errors/malformed_ahpacket_exception.h31
-rwxr-xr-xlibtransport/src/hicn/transport/errors/malformed_name_exception.h31
-rwxr-xr-xlibtransport/src/hicn/transport/errors/malformed_packet_exception.h29
-rwxr-xr-xlibtransport/src/hicn/transport/errors/not_implemented_exception.h30
-rwxr-xr-xlibtransport/src/hicn/transport/errors/null_pointer_exception.h31
-rwxr-xr-xlibtransport/src/hicn/transport/errors/runtime_exception.h32
-rwxr-xr-xlibtransport/src/hicn/transport/errors/tokenizer_exception.h31
-rwxr-xr-xlibtransport/src/hicn/transport/http/CMakeLists.txt36
-rwxr-xr-xlibtransport/src/hicn/transport/http/callbacks.h46
-rwxr-xr-xlibtransport/src/hicn/transport/http/client_connection.cc194
-rwxr-xr-xlibtransport/src/hicn/transport/http/client_connection.h82
-rwxr-xr-xlibtransport/src/hicn/transport/http/default_values.h32
-rwxr-xr-xlibtransport/src/hicn/transport/http/facade.h22
-rwxr-xr-xlibtransport/src/hicn/transport/http/message.h58
-rwxr-xr-xlibtransport/src/hicn/transport/http/request.cc83
-rwxr-xr-xlibtransport/src/hicn/transport/http/request.h61
-rwxr-xr-xlibtransport/src/hicn/transport/http/response.cc134
-rwxr-xr-xlibtransport/src/hicn/transport/http/response.h58
-rwxr-xr-xlibtransport/src/hicn/transport/http/server_acceptor.cc112
-rwxr-xr-xlibtransport/src/hicn/transport/http/server_acceptor.h62
-rwxr-xr-xlibtransport/src/hicn/transport/http/server_publisher.cc173
-rwxr-xr-xlibtransport/src/hicn/transport/http/server_publisher.h72
-rwxr-xr-xlibtransport/src/hicn/transport/interfaces/CMakeLists.txt38
-rwxr-xr-xlibtransport/src/hicn/transport/interfaces/async_transport.h640
-rwxr-xr-xlibtransport/src/hicn/transport/interfaces/full_duplex_socket.cc490
-rwxr-xr-xlibtransport/src/hicn/transport/interfaces/full_duplex_socket.h254
-rwxr-xr-xlibtransport/src/hicn/transport/interfaces/publication_options.h34
-rwxr-xr-xlibtransport/src/hicn/transport/interfaces/rtc_socket_consumer.cc35
-rwxr-xr-xlibtransport/src/hicn/transport/interfaces/rtc_socket_consumer.h35
-rwxr-xr-xlibtransport/src/hicn/transport/interfaces/rtc_socket_producer.cc157
-rwxr-xr-xlibtransport/src/hicn/transport/interfaces/rtc_socket_producer.h60
-rwxr-xr-xlibtransport/src/hicn/transport/interfaces/socket.h270
-rwxr-xr-xlibtransport/src/hicn/transport/interfaces/socket_consumer.cc735
-rwxr-xr-xlibtransport/src/hicn/transport/interfaces/socket_consumer.h259
-rwxr-xr-xlibtransport/src/hicn/transport/interfaces/socket_options_default_values.h68
-rwxr-xr-xlibtransport/src/hicn/transport/interfaces/socket_options_keys.h108
-rwxr-xr-xlibtransport/src/hicn/transport/interfaces/socket_producer.cc948
-rwxr-xr-xlibtransport/src/hicn/transport/interfaces/socket_producer.h269
-rwxr-xr-xlibtransport/src/hicn/transport/portability/CMakeLists.txt26
-rwxr-xr-xlibtransport/src/hicn/transport/portability/c_portability.h36
-rwxr-xr-xlibtransport/src/hicn/transport/portability/portability.h46
-rwxr-xr-xlibtransport/src/hicn/transport/protocols/CMakeLists.txt46
-rwxr-xr-xlibtransport/src/hicn/transport/protocols/cbr.cc47
-rwxr-xr-xlibtransport/src/hicn/transport/protocols/cbr.h48
-rwxr-xr-xlibtransport/src/hicn/transport/protocols/consumer.conf21
-rwxr-xr-xlibtransport/src/hicn/transport/protocols/download_observer.h32
-rwxr-xr-xlibtransport/src/hicn/transport/protocols/protocol.cc45
-rwxr-xr-xlibtransport/src/hicn/transport/protocols/protocol.h79
-rwxr-xr-xlibtransport/src/hicn/transport/protocols/raaqm.cc416
-rwxr-xr-xlibtransport/src/hicn/transport/protocols/raaqm.h94
-rwxr-xr-xlibtransport/src/hicn/transport/protocols/raaqm_data_path.cc158
-rwxr-xr-xlibtransport/src/hicn/transport/protocols/raaqm_data_path.h230
-rwxr-xr-xlibtransport/src/hicn/transport/protocols/rate_estimation.cc353
-rwxr-xr-xlibtransport/src/hicn/transport/protocols/rate_estimation.h175
-rwxr-xr-xlibtransport/src/hicn/transport/protocols/rtc.cc813
-rwxr-xr-xlibtransport/src/hicn/transport/protocols/rtc.h210
-rwxr-xr-xlibtransport/src/hicn/transport/protocols/rtc_data_path.cc85
-rwxr-xr-xlibtransport/src/hicn/transport/protocols/rtc_data_path.h62
-rwxr-xr-xlibtransport/src/hicn/transport/protocols/test/CMakeLists.txt10
-rwxr-xr-xlibtransport/src/hicn/transport/protocols/test/test_transport_producer.cc80
-rwxr-xr-xlibtransport/src/hicn/transport/protocols/vegas.cc630
-rwxr-xr-xlibtransport/src/hicn/transport/protocols/vegas.h161
-rwxr-xr-xlibtransport/src/hicn/transport/protocols/vegas_rto_estimator.cc57
-rwxr-xr-xlibtransport/src/hicn/transport/protocols/vegas_rto_estimator.h48
-rwxr-xr-xlibtransport/src/hicn/transport/utils/CMakeLists.txt76
-rwxr-xr-xlibtransport/src/hicn/transport/utils/array.h62
-rwxr-xr-xlibtransport/src/hicn/transport/utils/branch_prediction.h22
-rwxr-xr-xlibtransport/src/hicn/transport/utils/content_store.cc109
-rwxr-xr-xlibtransport/src/hicn/transport/utils/content_store.h75
-rwxr-xr-xlibtransport/src/hicn/transport/utils/conversions.h37
-rwxr-xr-xlibtransport/src/hicn/transport/utils/crypto_hash.h115
-rwxr-xr-xlibtransport/src/hicn/transport/utils/crypto_hash_type.h31
-rwxr-xr-xlibtransport/src/hicn/transport/utils/crypto_hasher.h68
-rwxr-xr-xlibtransport/src/hicn/transport/utils/crypto_suite.h35
-rwxr-xr-xlibtransport/src/hicn/transport/utils/daemonizator.cc73
-rwxr-xr-xlibtransport/src/hicn/transport/utils/daemonizator.h25
-rwxr-xr-xlibtransport/src/hicn/transport/utils/deadline_timer.h114
-rwxr-xr-xlibtransport/src/hicn/transport/utils/endianess.h136
-rwxr-xr-xlibtransport/src/hicn/transport/utils/epoll_event_reactor.cc183
-rwxr-xr-xlibtransport/src/hicn/transport/utils/epoll_event_reactor.h65
-rwxr-xr-xlibtransport/src/hicn/transport/utils/event_reactor.h37
-rwxr-xr-xlibtransport/src/hicn/transport/utils/event_thread.h101
-rwxr-xr-xlibtransport/src/hicn/transport/utils/fd_deadline_timer.h127
-rwxr-xr-xlibtransport/src/hicn/transport/utils/hash.h101
-rwxr-xr-xlibtransport/src/hicn/transport/utils/identity.cc130
-rwxr-xr-xlibtransport/src/hicn/transport/utils/identity.h64
-rwxr-xr-xlibtransport/src/hicn/transport/utils/key_id.h25
-rwxr-xr-xlibtransport/src/hicn/transport/utils/linux.h64
-rwxr-xr-xlibtransport/src/hicn/transport/utils/literals.h55
-rwxr-xr-xlibtransport/src/hicn/transport/utils/log.cc1405
-rwxr-xr-xlibtransport/src/hicn/transport/utils/log.h1057
-rwxr-xr-xlibtransport/src/hicn/transport/utils/membuf.cc864
-rwxr-xr-xlibtransport/src/hicn/transport/utils/membuf.h916
-rwxr-xr-xlibtransport/src/hicn/transport/utils/min_filter.h56
-rwxr-xr-xlibtransport/src/hicn/transport/utils/object_pool.h76
-rwxr-xr-xlibtransport/src/hicn/transport/utils/ring_buffer.h129
-rwxr-xr-xlibtransport/src/hicn/transport/utils/sharable_vector.h30
-rwxr-xr-xlibtransport/src/hicn/transport/utils/signer.cc173
-rwxr-xr-xlibtransport/src/hicn/transport/utils/signer.h69
-rwxr-xr-xlibtransport/src/hicn/transport/utils/socket.h267
-rwxr-xr-xlibtransport/src/hicn/transport/utils/spinlock.h53
-rwxr-xr-xlibtransport/src/hicn/transport/utils/stream_buffer.h31
-rwxr-xr-xlibtransport/src/hicn/transport/utils/string_tokenizer.cc47
-rwxr-xr-xlibtransport/src/hicn/transport/utils/string_tokenizer.h35
-rwxr-xr-xlibtransport/src/hicn/transport/utils/test.h46
-rwxr-xr-xlibtransport/src/hicn/transport/utils/uri.cc122
-rwxr-xr-xlibtransport/src/hicn/transport/utils/uri.h47
-rwxr-xr-xlibtransport/src/hicn/transport/utils/verifier.cc193
-rwxr-xr-xlibtransport/src/hicn/transport/utils/verifier.h85
-rwxr-xr-xutils/CMakeLists.txt54
-rwxr-xr-xutils/src/hiperf.cc724
-rwxr-xr-xutils/src/ping_client.cc428
-rwxr-xr-xutils/src/ping_server.cc300
556 files changed, 94971 insertions, 0 deletions
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é <jordan.auge@cisco.com>
+Alberto Compagno <acompagn@cisco.com>
+Giovanni Conte <gconte@cisco.com>
+Luca Muscariello <lumuscar@cisco.com>
+Michele Papalini <mpapal@cisco.com>
+Mauro Sardara <msardara@cisco.com>
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 <protocol> <symbolic> <local_adress> <local_port>
+
+ <symbolic> :User defined name for listener, must start with alpha and bealphanum
+ <protocol> :tcp | udp
+ <localAddress> :IPv4 or IPv6 address
+ <local_port> :TCP/UDP port
+
+```
+
+`add listener hicn`: creates a hicn listener with the specified options on the local forwarder.
+
+```
+add listener hicn <symbolic> <local_adress>
+
+ <symbolic> :User defined name for listener, must start with alpha and be alphanum
+ <localAddress> :IPv4 or IPv6 address
+
+```
+
+`add connection`: creates a TCP or UDP connection on the local forwarder with the specified options.
+
+```
+add connection <protocol> <symbolic> <remote_ip> <remote_port> <local_ip> <local_port>
+
+ <protocol> : tcp | udp
+ <symbolic> : symbolic name, e.g. 'conn1' (must be unique, start with alpha)
+ <remote_ip> : the IPv4 or IPv6 of the remote system
+ <remote_port> : the remote TCP/UDP port
+ <local_ip> : local IP address to bind to
+ <local_port> : local TCP/UDP port
+
+```
+`add connection hicn`: creates an hicn connection on the local forwarder with the specified options.
+
+```
+add connection hicn <symbolic> <remote_ip> <local_ip>
+
+ <symbolic> : symbolic name, e.g. 'conn1' (must be unique, start with alpha)
+ <remote_ip> : the IPv4 or IPv6 of the remote system
+ <local_ip> : local IP address to bind to
+
+```
+`list`: lists the connections, routes or listeners available on the local hicn-light forwarder
+```
+list <connections | routes | listeners>
+
+```
+`add route`: adds a route to the specified connection
+
+```
+add route <symbolic | connid> <prefix> <cost>
+
+ <symbolic> :The symbolic name for an exgress (must be unique, start with alpha)
+ <connid>: :The egress connection id (see 'help list connections')
+ <prefix>: :ipAddress/netmask
+ <cost>: :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 <protocol> <symbolic | connid>
+
+ <protocol> : tcp | upd. This is the protocol used to create the connection.
+ <symbolic> :The symbolic name for an exgress (must be unique, start with alpha)
+ <connid>: :The egress connection id (see 'help list connections')
+
+```
+
+`remove route`: remove the specified prefix for a local connection
+
+```
+remove route <symbolic | connid> <prefix>
+
+ <connid> : the alphanumeric name of a local connection
+ <prefix> : the prefix (ipAddress/netmask) to remove
+```
+
+`cache serve`: enables/disables replies from local content store (if available)
+
+```
+cache serve <on|off>
+```
+`cache store`: enables/disables the storage of incoming data packets in the local content store
+(if available)
+
+```
+cache store <on|off>
+
+```
+`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 <prefix> <strategy>
+
+ <preifx> : the prefix to which apply the forwarding strategy
+ <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 <on|off> <symbolic | connid>
+
+ <symbolic> :The symbolic name for an exgress (must be unique, start with alpha)
+ <connid>: :The egress connection id (see 'help list connections')
+
+```
+`add punting`: Add punting rules to the forwarders.
+
+```
+add punting <symbolic> <prefix>
+
+ <symbolic> : listener symbolic name
+ <address> : prefix to add as a punting rule. (example 1234::0/64)
+```
+`mapme enable`: enables/disables mapme
+
+```
+mapme enable <on|off>
+```
+`mapme discovery`: enables/disables mapme discovery
+
+```
+mapme discovery <on|off>
+```
+
+`mapme timescale`: set the timescale value expressed in millisencods
+
+```
+mapme timescale <milliseconds>
+```
+`mapme retx`: set the retrasmission time value expressed in millisecond
+
+```
+mapme retx <milliseconds>
+```
+`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 <src/config.h>
+
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <parc/assert/parc_Assert.h>
+#include <string.h>
+
+#include <parc/security/parc_IdentityFile.h>
+#include <parc/security/parc_Security.h>
+
+#include <parc/algol/parc_ArrayList.h>
+#include <parc/algol/parc_List.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+#include <src/core/dispatcher.h>
+#include <src/core/forwarder.h>
+
+#include <errno.h>
+#include <src/config/controlRoot.h>
+#include <src/config/controlState.h>
+
+#include <src/utils/commands.h>
+
+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 <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+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 <errno.h>
+#include <fcntl.h>
+#include <src/config.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <parc/algol/parc_FileOutputStream.h>
+#include <parc/logging/parc_LogLevel.h>
+#include <parc/logging/parc_LogReporterFile.h>
+#include <parc/logging/parc_LogReporterTextStdout.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <src/core/dispatcher.h>
+#include <src/core/forwarder.h>
+
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+#include <string.h>
+
+#ifndef __ANDROID__
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#else
+extern int errno;
+#endif
+#endif
+
+#include <parc/algol/parc_Memory.h>
+
+#include <src/config/commandOps.h>
+#include <src/config/commandParser.h>
+
+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 <parc/algol/parc_List.h>
+
+#include <src/config/commandReturn.h>
+
+// 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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+#include <string.h>
+
+#include <parc/security/parc_Security.h>
+
+#include <parc/algol/parc_List.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Time.h>
+#include <parc/algol/parc_TreeRedBlack.h>
+
+#include <src/config/commandParser.h>
+
+#ifndef __ANDROID__
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#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 <src/config/commandOps.h>
+#include <src/config/commandReturn.h>
+
+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 <arpa/inet.h>
+#include <ctype.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <parc/algol/parc_HashMap.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_String.h>
+
+#include <src/config/configurationListeners.h>
+#include <src/config/symbolicNameTable.h>
+
+#include <src/core/connection.h>
+#include <src/core/connectionTable.h>
+#include <src/core/forwarder.h>
+#include <src/core/system.h>
+#ifdef WITH_MAPME
+#include <src/core/mapMe.h>
+#endif /* WITH_MAPME */
+
+#include <src/io/streamConnection.h>
+
+#include <src/io/hicnTunnel.h>
+#include <src/io/tcpTunnel.h>
+#include <src/io/udpTunnel.h>
+
+#include <parc/algol/parc_Unsigned.h>
+#include <src/io/listener.h> //the listener list
+#include <src/io/listenerSet.h> // needed to print
+#include <src/utils/commands.h>
+#include <src/utils/utils.h>
+
+#include <src/utils/address.h>
+
+#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 <src/utils/commands.h>
+#include <src/core/logger.h>
+
+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 <ctype.h>
+#include <errno.h>
+#include <src/config.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <parc/algol/parc_ArrayList.h>
+#include <parc/algol/parc_List.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Object.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/config/configuration.h>
+#include <src/config/configurationFile.h>
+#include <src/config/controlRoot.h>
+#include <src/config/controlState.h>
+
+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 **)&copy);
+ }
+ }
+ }
+
+ 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 <src/core/forwarder.h>
+
+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 <arpa/inet.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Network.h>
+
+#include <src/core/system.h>
+#include <src/utils/interfaceSet.h>
+#include <src/utils/punting.h>
+
+#include <src/config/configurationListeners.h>
+#include <src/io/hicnListener.h>
+#include <src/io/tcpListener.h>
+#include <src/io/udpListener.h>
+
+#include <src/utils/addressList.h>
+#include <src/utils/commands.h>
+#include <src/utils/utils.h>
+
+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 <src/config/configuration.h>
+#include <src/core/forwarder.h>
+
+#include <src/utils/address.h>
+
+/**
+ * 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 <src/config.h>
+
+#include <parc/assert/parc_Assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/algol/parc_Memory.h>
+
+#include <src/config/controlAdd.h>
+#include <src/config/controlAddConnection.h>
+#include <src/config/controlAddListener.h>
+#include <src/config/controlAddPunting.h>
+#include <src/config/controlAddRoute.h>
+
+// ===================================================
+
+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 <src/config/controlState.h>
+
+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 <src/config.h>
+
+#include <ctype.h>
+#include <parc/assert/parc_Assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Network.h>
+
+#include <src/config/controlAddConnection.h>
+
+#include <src/utils/commands.h>
+#include <src/utils/utils.h>
+
+// ===================================================
+
+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 <symbolic> <remote_ip> <local_ip>\n");
+ printf(
+ "add connection udp <symbolic> <remote_ip> <port> <local_ip> <port>\n");
+ printf(
+ " <symbolic> : symbolic name, e.g. 'conn1' (must be "
+ "unique, start with alpha)\n");
+ printf(
+ " <remote_ip> : the IPv4 or IPv6 or hostname of the remote system\n");
+ printf(" <local_ip> : 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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Network.h>
+
+#include <src/config/controlAddListener.h>
+
+#include <src/utils/commands.h>
+#include <src/utils/utils.h>
+
+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 <symbolic> <localAddress> \n");
+ printf(" add listener udp <symbolic> <localAddress> <port> \n");
+ printf(" add listener tcp <symbolic> <localAddress> <port> \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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Network.h>
+#include <src/utils/punting.h>
+
+#include <src/config/controlAddPunting.h>
+
+#include <src/utils/commands.h>
+#include <src/utils/utils.h>
+
+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 <symbolic> <prefix>\n");
+ printf(" <symbolic> : listener symbolic name\n");
+ printf(
+ " <address> : 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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Network.h>
+
+#include <src/config/controlAddRoute.h>
+
+#include <src/utils/commands.h>
+#include <src/utils/utils.h>
+
+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 <symbolic | connid> <prefix> <cost>\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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/security/parc_Security.h>
+
+#include <parc/algol/parc_Memory.h>
+
+#include <src/config/controlCache.h>
+#include <src/config/controlCacheClear.h>
+#include <src/config/controlCacheServe.h>
+#include <src/config/controlCacheStore.h>
+
+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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_Memory.h>
+
+#include <src/config/controlCacheClear.h>
+
+#include <src/utils/commands.h>
+#include <src/utils/utils.h>
+
+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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_Memory.h>
+
+#include <src/config/controlCacheServe.h>
+
+#include <src/utils/commands.h>
+#include <src/utils/utils.h>
+
+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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_Memory.h>
+
+#include <src/config/controlCacheStore.h>
+
+#include <src/utils/commands.h>
+#include <src/utils/utils.h>
+
+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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/security/parc_Security.h>
+
+#include <parc/algol/parc_Memory.h>
+
+#include <src/config/controlList.h>
+#include <src/config/controlListConnections.h>
+//#include <src/config/controlListInterfaces.h>
+#include <src/config/controlListListeners.h>
+#include <src/config/controlListRoutes.h>
+
+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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_Memory.h>
+
+#include <src/config/controlListConnections.h>
+
+#include <src/utils/commands.h>
+#include <src/utils/utils.h>
+
+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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_Memory.h>
+
+#include <src/config/controlListInterfaces.h>
+
+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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+
+#include <src/config/controlListListeners.h>
+#include <src/utils/commands.h>
+#include <src/utils/utils.h>
+
+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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Time.h>
+
+#include <src/config/controlListRoutes.h>
+
+#include <src/utils/commands.h>
+#include <src/utils/utils.h>
+
+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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+
+#include <src/config/controlMapMe.h>
+#include <src/config/controlMapMeDiscovery.h>
+#include <src/config/controlMapMeEnable.h>
+#include <src/config/controlMapMeRetx.h>
+#include <src/config/controlMapMeTimescale.h>
+
+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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/config/controlMapMeDiscovery.h>
+
+#include <src/utils/commands.h>
+#include <src/utils/utils.h>
+
+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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/config/controlMapMeEnable.h>
+
+#include <src/utils/commands.h>
+#include <src/utils/utils.h>
+
+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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/config/controlMapMeRetx.h>
+
+#include <src/utils/commands.h>
+#include <src/utils/utils.h>
+
+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 <milliseconds>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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/config/controlMapMeTimescale.h>
+
+#include <src/utils/commands.h>
+#include <src/utils/utils.h>
+
+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 <milliseconds>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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/security/parc_Security.h>
+
+#include <src/config/controlQuit.h>
+
+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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/security/parc_Security.h>
+
+#include <parc/algol/parc_Memory.h>
+
+#include <src/config/controlRemove.h>
+#include <src/config/controlRemoveConnection.h>
+#include <src/config/controlRemovePunting.h>
+#include <src/config/controlRemoveRoute.h>
+
+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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Network.h>
+#include <src/utils/address.h>
+
+#include <src/config/controlRemoveConnection.h>
+
+#include <src/utils/commands.h>
+#include <src/utils/utils.h>
+
+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 <symbolic|id>\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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Network.h>
+#include <src/utils/address.h>
+
+#include <src/config/controlRemovePunting.h>
+
+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 <symbolic> <prefix>\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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_List.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Network.h>
+
+#include <src/utils/address.h>
+
+#include <src/config/controlRemoveRoute.h>
+
+#include <src/utils/commands.h>
+#include <src/utils/utils.h>
+
+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 <symbolic | connid> <prefix>\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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <src/config/controlAdd.h>
+#include <src/config/controlCache.h>
+#include <src/config/controlList.h>
+#include <src/config/controlMapMe.h>
+#include <src/config/controlQuit.h>
+#include <src/config/controlRemove.h>
+#include <src/config/controlRoot.h>
+#include <src/config/controlSet.h>
+#include <src/config/controlUnset.h>
+
+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 <keystorepath>] [--password <password>] "
+ "command\n");
+ printf("\n");
+ printf("Interactive execution:\n");
+ printf(" controller [--keystore <keystorepath>] [--password <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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/security/parc_Security.h>
+
+#include <src/config/controlSet.h>
+#include <src/config/controlSetDebug.h>
+#include <src/config/controlSetStrategy.h>
+#include <src/config/controlSetWldr.h>
+
+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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_Memory.h>
+
+#include <src/config/controlSetDebug.h>
+#include <src/core/dispatcher.h>
+#include <src/core/forwarder.h>
+
+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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Network.h>
+
+#include <src/config/controlSetDebug.h>
+#include <src/core/dispatcher.h>
+#include <src/core/forwarder.h>
+
+#include <src/utils/commands.h>
+#include <src/utils/utils.h>
+
+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 <prefix> <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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_Memory.h>
+
+#include <src/config/controlSetDebug.h>
+#include <src/core/dispatcher.h>
+#include <src/core/forwarder.h>
+
+#include <src/utils/commands.h>
+#include <src/utils/utils.h>
+
+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 <on|off> <connection_id>\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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+#include <string.h>
+
+#include <parc/security/parc_Security.h>
+
+#include <parc/algol/parc_List.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Network.h>
+#include <parc/algol/parc_Time.h>
+#include <parc/algol/parc_TreeRedBlack.h>
+
+#include <src/config/commandParser.h>
+#include <src/config/controlRoot.h>
+#include <src/config/controlState.h>
+
+#include <src/utils/commands.h>
+
+#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 <parc/algol/parc_List.h>
+#include <src/config/commandParser.h>
+
+#include <src/utils/commands.h>
+
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/security/parc_Security.h>
+
+#include <src/config/controlUnset.h>
+#include <src/config/controlUnsetDebug.h>
+
+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 <src/config/controlState.h>
+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 <src/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_Memory.h>
+
+#include <src/config/controlUnsetDebug.h>
+#include <src/core/dispatcher.h>
+#include <src/core/forwarder.h>
+
+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 <src/config/controlState.h>
+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 <ctype.h>
+#include <parc/algol/parc_Hash.h>
+#include <parc/algol/parc_HashCodeTable.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/config.h>
+#include <stdio.h>
+
+#include <src/config/symbolicNameTable.h>
+
+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 <stdbool.h>
+
+/**
+ * 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 <src/config.h>
+#include <stdio.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <src/content_store/contentStoreEntry.h>
+
+#include <parc/assert/parc_Assert.h>
+
+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 <src/content_store/listLRU.h>
+#include <src/core/message.h>
+
+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
+ * <code>contentStoreEntry_Destroy()</code> 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 <src/config.h>
+#include <stdio.h>
+
+#include <src/content_store/contentStoreInterface.h>
+
+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 <stdio.h>
+
+#include <src/core/message.h>
+
+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 <src/config.h>
+#include <stdio.h>
+#include <sys/queue.h>
+
+#include <parc/algol/parc_DisplayIndented.h>
+#include <parc/algol/parc_HashCodeTable.h>
+#include <parc/algol/parc_Object.h>
+
+#include <src/core/logger.h>
+
+#include <src/content_store/contentStoreLRU.h>
+
+#include <src/content_store/contentStoreEntry.h>
+#include <src/content_store/contentStoreInterface.h>
+#include <src/content_store/listLRU.h>
+#include <src/content_store/listTimeOrdered.h>
+
+#include <parc/assert/parc_Assert.h>
+#include <src/processor/hashTableFunction.h>
+
+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 <src/content_store/contentStoreInterface.h>
+#include <src/core/logger.h>
+#include <stdio.h>
+
+/**
+ * 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 <src/config.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/queue.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/content_store/listLRU.h>
+
+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 <code>LruEntry_Destroy()</code> 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 <parc/assert/parc_Assert.h>
+#include <src/config.h>
+#include <src/content_store/listTimeOrdered.h>
+
+#include <parc/algol/parc_Object.h>
+#include <parc/algol/parc_TreeRedBlack.h>
+
+/**
+ * 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 <parc/algol/parc_TreeRedBlack.h>
+#include <src/content_store/contentStoreEntry.h>
+#include <src/core/message.h>
+#include <stdio.h>
+
+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 <limits.h>
+#include <src/config.h>
+#include <stdio.h>
+
+#include <src/core/connection.h>
+#include <src/core/messageHandler.h>
+#include <src/core/ticks.h>
+#include <src/core/wldr.h>
+#include <src/io/addressPair.h>
+#include <src/io/ioOperations.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+
+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 <src/config.h>
+#include <src/io/ioOperations.h>
+#include <src/utils/address.h>
+
+// 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 <src/config.h>
+#include <stdio.h>
+
+#include <parc/algol/parc_ArrayList.h>
+#include <parc/algol/parc_Memory.h>
+
+#include <parc/assert/parc_Assert.h>
+#include <src/core/connectionList.h>
+
+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 <src/core/connection.h>
+
+/**
+ * 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 <src/config.h>
+#include <src/core/connectionManager.h>
+#include <src/core/forwarder.h>
+#include <src/messenger/messenger.h>
+#include <src/messenger/messengerRecipient.h>
+#include <src/messenger/missiveDeque.h>
+#include <stdio.h>
+
+#include <parc/algol/parc_Memory.h>
+
+#include <parc/assert/parc_Assert.h>
+
+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 <src/core/forwarder.h>
+
+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 <src/config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_ArrayList.h>
+#include <parc/algol/parc_Hash.h>
+#include <parc/algol/parc_HashCodeTable.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_TreeRedBlack.h>
+#include <src/core/connectionTable.h>
+#include <src/io/addressPair.h>
+
+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 <src/core/connection.h>
+#include <src/core/connectionList.h>
+#include <src/io/addressPair.h>
+#include <src/io/ioOperations.h>
+
+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 <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <src/config.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#include <parc/algol/parc_EventQueue.h>
+#include <parc/algol/parc_EventTimer.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <src/core/dispatcher.h>
+
+#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 <stdbool.h>
+#include <sys/socket.h>
+
+struct dispatcher;
+typedef struct dispatcher Dispatcher;
+
+#include <parc/algol/parc_Event.h>
+#include <parc/algol/parc_EventQueue.h>
+#include <parc/algol/parc_EventScheduler.h>
+#include <parc/algol/parc_EventSignal.h>
+#include <parc/algol/parc_EventSocket.h>
+#include <parc/algol/parc_EventTimer.h>
+#include <parc/algol/parc_Memory.h>
+
+#include <src/core/logger.h>
+
+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 <code>dispatcher_WaitForStopped()</code> 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 <code>dispatcher_Stop()</code> 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 <code>forwarder_NewBind()</code>
+ * that received the client connection
+ * @constant client_socket is the client socket
+ * @constant user_data is the user_data passed to
+ * <code>forwarder_NewBind()</code>
+ * @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
+ * <code>Forwarder_CreateEvent()</code>
+ */
+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
+ * <code>forwarder_StartTimer()</code>
+ *
+ * @param isPeriodic means the timer will fire repeatidly, otherwise it is a
+ * one-shot and needs to be set again with <code>dispatcher_StartTimer()</code>
+ */
+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
+ * <code>forwarder_StartNetworkEvent()</code>
+ *
+ * @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
+ * <code>forwarder_StartSignalEvent()</code>
+ *
+ * @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 <src/core/streamBuffer.h>
+#include <src/io/addressPair.h>
+
+/**
+ * @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 <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <src/config.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
+#include <parc/algol/parc_ArrayList.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Object.h>
+#include <parc/logging/parc_LogReporterTextStdout.h>
+
+#include <src/core/connectionManager.h>
+#include <src/core/connectionTable.h>
+#include <src/core/dispatcher.h>
+#include <src/core/forwarder.h>
+#include <src/core/messagePacketType.h>
+#ifdef WITH_MAPME
+#include <src/core/mapMe.h>
+#endif /* WITH_MAPME */
+#include <src/config/configuration.h>
+#include <src/config/configurationFile.h>
+#include <src/config/configurationListeners.h>
+#include <src/processor/messageProcessor.h>
+
+#include <src/core/wldr.h>
+
+#include <parc/assert/parc_Assert.h>
+
+// 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 <stdlib.h>
+#include <sys/time.h>
+
+#include <src/core/connectionTable.h>
+#include <src/core/dispatcher.h>
+#include <src/messenger/messenger.h>
+
+#include <src/core/message.h>
+
+#include <src/config/configuration.h>
+
+#ifdef WITH_MAPME
+#include <src/processor/fib.h>
+#endif /* WITH_MAPME */
+
+#include <src/core/logger.h>
+#include <src/core/ticks.h>
+#include <src/io/listenerSet.h>
+
+#include <src/processor/fibEntryList.h>
+
+#include <parc/algol/parc_Clock.h>
+
+#include <src/socket/api.h>
+
+#define PORT_NUMBER 9695
+#define PORT_NUMBER_AS_STRING "9695"
+
+#include <src/utils/commands.h>
+
+// ==============================================
+
+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 <errno.h>
+#include <src/config.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Object.h>
+
+#include <parc/logging/parc_Log.h>
+
+#include <src/core/forwarder.h>
+#include <src/core/logger.h>
+
+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 <parc/algol/parc_Buffer.h>
+#include <parc/logging/parc_LogLevel.h>
+#include <parc/logging/parc_LogReporter.h>
+#include <stdarg.h>
+#include <sys/time.h>
+
+#include <parc/algol/parc_Clock.h>
+
+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 <hicn/hicn.h>
+#include <src/core/mapMe.h>
+#include <stdio.h> // printf
+
+#include <src/core/connectionList.h>
+#include <src/core/forwarder.h>
+#include <src/core/logger.h>
+#include <src/core/message.h>
+#include <src/core/messagePacketType.h> // packet types
+#include <src/core/ticks.h>
+#include <src/processor/fibEntry.h>
+#include <src/processor/pitEntry.h>
+
+#include <parc/algol/parc_HashMap.h>
+#include <parc/algol/parc_Iterator.h>
+#include <parc/algol/parc_Unsigned.h>
+#include <parc/assert/parc_Assert.h>
+
+#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 <src/core/connection.h>
+
+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, &params);
+ 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, &params);
+
+ // XXX Dispatch message dependenging on type
+ switch (params.type) {
+ case UPDATE:
+ case NOTIFICATION:
+ mapMe_onSpecialInterest(mapme, msgBuffer, conn_id, &prefix, &params);
+ break;
+ case UPDATE_ACK:
+ case NOTIFICATION_ACK:
+ mapMe_onSpecialInterestAck(mapme, msgBuffer, conn_id, &prefix, &params);
+ 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 <stdbool.h>
+#include <stdint.h>
+
+#include <src/io/hicnListener.h>
+
+#include <hicn/hicn.h>
+#include <src/utils/commands.h>
+
+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 <errno.h>
+#include <src/config.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <src/core/forwarder.h>
+#include <src/core/message.h>
+#include <src/core/wldr.h>
+
+#include <src/core/messageHandler.h>
+
+#include <parc/algol/parc_Hash.h>
+#include <parc/algol/parc_Memory.h>
+#include <src/core/messagePacketType.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_EventBuffer.h>
+
+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 <src/config.h>
+#include <src/core/logger.h>
+#include <src/core/messagePacketType.h>
+#include <src/core/streamBuffer.h>
+
+#include <src/core/name.h>
+
+#include <parc/algol/parc_EventBuffer.h>
+#include <parc/algol/parc_EventQueue.h>
+
+#include <src/utils/address.h>
+
+#include <src/core/ticks.h>
+
+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 <stdlib.h>
+
+#include <hicn/hicn.h>
+#include <src/core/messagePacketType.h>
+
+#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 <limits.h>
+#include <src/config.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <parc/algol/parc_BufferComposer.h>
+#include <parc/algol/parc_Hash.h>
+#include <parc/algol/parc_Memory.h>
+
+#include <src/core/messageHandler.h>
+#include <src/core/name.h>
+
+#include <parc/algol/parc_Hash.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#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 <stdbool.h>
+#include <stdlib.h>
+
+#include <src/core/messagePacketType.h>
+#include <src/core/nameBitvector.h>
+#include <src/utils/address.h>
+
+#include <src/utils/commands.h>
+
+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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+
+#include <src/core/messageHandler.h>
+#include <src/core/nameBitvector.h>
+
+#include <parc/algol/parc_Hash.h>
+
+#include <src/utils/commands.h>
+
+#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 <hicn/hicn.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <src/utils/address.h>
+
+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 <parc/algol/parc_ArrayList.h>
+#include <parc/algol/parc_Memory.h>
+#include <src/config.h>
+#include <src/core/numberSet.h>
+#include <stdio.h>
+
+#include <parc/assert/parc_Assert.h>
+
+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 <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+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 <code>setToAdd</code> to <code>destinationSet</code>
+ * @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 <code>difference = minuend -
+ * subtrahend</code>, returns a new number set.
+ * @discussion
+ * <code>minuend</code> and <code>subtrahend</code> are not modified. A new
+ * difference set is created.
+ *
+ * Returns the elements in <code>minuend</code> that are not in
+ * <code>subtrahend</code>.
+ *
+ * @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 <src/config.h>
+#include <stdio.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <src/core/streamBuffer.h>
+
+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 <parc/algol/parc_EventQueue.h>
+#include <stdbool.h>
+
+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 <code>low</code> bytes
+ * available to read, the read callback will be fired. If the bytes in the
+ * buffer exceed <code>high</code>, the stream buffer will stop reading from the
+ * network.
+ *
+ * For a write watermark, when the bytes in the buffer fall below
+ * <code>low</code>, the write callback is fired. The <code>high</code>
+ * 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
+ * <code>StreamBuffer_SetCallbacks()</code>
+ */
+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
+ * <code>StreamBuffer_SetCallbacks()</code>
+ */
+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 <src/core/forwarder.h>
+#include <src/utils/interfaceSet.h>
+
+/**
+ * @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 <stdint.h>
+
+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 <parc/assert/parc_Assert.h>
+#include <parc/logging/parc_LogReporterTextStdout.h>
+#include <src/core/connection.h>
+#include <src/core/forwarder.h>
+#include <src/core/wldr.h>
+#include <stdint.h>
+#include <stdio.h>
+
+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 <src/config.h>
+#include <src/core/connection.h>
+#include <src/core/message.h>
+
+#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 <src/config.h>
+#include <stdio.h>
+
+#include <parc/algol/parc_Hash.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Object.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/io/addressPair.h>
+
+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 <src/utils/address.h>
+
+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 <errno.h>
+#include <src/config.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <src/core/message.h>
+#include <src/io/hicnConnection.h>
+
+#include <src/core/messageHandler.h>
+
+#include <parc/algol/parc_Hash.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/core/connection.h>
+#include <src/core/forwarder.h>
+
+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 <src/core/forwarder.h>
+#include <src/io/addressPair.h>
+#include <src/io/ioOperations.h>
+#include <src/utils/address.h>
+
+/**
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <src/config.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <unistd.h>
+
+#include <src/io/hicnConnection.h>
+#include <src/io/hicnListener.h>
+
+#include <src/core/connection.h>
+#include <src/core/connectionTable.h>
+#include <src/core/forwarder.h>
+#ifdef WITH_MAPME
+#include <src/config/symbolicNameTable.h>
+#include <src/core/mapMe.h>
+#include <src/core/message.h>
+#include <src/io/hicnTunnel.h>
+#endif /* WITH_MAPME */
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Network.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/core/mapMe.h>
+#include <src/core/messagePacketType.h>
+#include <src/io/listener.h>
+#include <src/socket/api.h>
+
+#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 <src/core/forwarder.h>
+#include <src/core/messageHandler.h>
+#include <src/io/listener.h>
+#include <stdlib.h>
+
+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 <errno.h>
+#include <src/config.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <parc/assert/parc_Assert.h>
+#include <src/io/hicnConnection.h>
+#include <src/io/hicnListener.h>
+#include <src/io/hicnTunnel.h>
+
+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 <src/core/forwarder.h>
+#include <src/io/ioOperations.h>
+#include <src/io/listener.h>
+#include <src/utils/address.h>
+
+/**
+ * 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 <parc/assert/parc_Assert.h>
+#include <src/config.h>
+#include <src/io/ioOperations.h>
+#include <stdio.h>
+
+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 <src/core/message.h>
+#include <src/core/ticks.h>
+#include <src/io/addressPair.h>
+#include <src/utils/address.h>
+
+// 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 <src/utils/address.h>
+
+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 <src/config.h>
+#include <stdio.h>
+
+#include <parc/algol/parc_ArrayList.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/io/listenerSet.h>
+
+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 <src/io/listener.h>
+
+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 <errno.h>
+#include <src/config.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <parc/algol/parc_Hash.h>
+#include <src/core/connection.h>
+#include <src/core/forwarder.h>
+#include <src/core/message.h>
+#include <src/io/streamConnection.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/core/messageHandler.h>
+
+#include <src/utils/commands.h>
+
+#include <hicn/hicn.h>
+// 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 <src/core/forwarder.h>
+#include <src/core/messagePacketType.h>
+#include <src/io/addressPair.h>
+#include <src/io/ioOperations.h>
+#include <src/utils/address.h>
+
+/**
+ * @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 <errno.h>
+#include <src/config.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <src/core/connectionTable.h>
+#include <src/core/forwarder.h>
+#include <src/io/listener.h>
+#include <src/io/streamConnection.h>
+#include <src/io/tcpListener.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Network.h>
+#include <parc/assert/parc_Assert.h>
+
+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 <netinet/in.h>
+#include <src/core/forwarder.h>
+#include <src/io/listener.h>
+#include <stdlib.h>
+
+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 <errno.h>
+#include <src/config.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <parc/assert/parc_Assert.h>
+#include <src/io/streamConnection.h>
+#include <src/io/tcpListener.h>
+#include <src/io/tcpTunnel.h>
+
+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 <src/core/forwarder.h>
+#include <src/io/ioOperations.h>
+#include <src/io/listener.h>
+#include <src/utils/address.h>
+
+/**
+ */
+// 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 <errno.h>
+#include <src/config.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <src/core/messageHandler.h>
+#include <src/io/udpConnection.h>
+
+#include <parc/algol/parc_Hash.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/core/connection.h>
+#include <src/core/forwarder.h>
+#include <src/core/message.h>
+
+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 <src/core/forwarder.h>
+#include <src/io/addressPair.h>
+#include <src/io/ioOperations.h>
+#include <src/utils/address.h>
+
+/**
+ * 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 <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <src/config.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <src/core/messageHandler.h>
+
+#include <src/io/udpConnection.h>
+#include <src/io/udpListener.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/core/connection.h>
+#include <src/core/forwarder.h>
+#include <src/core/messagePacketType.h>
+
+#ifdef WITH_MAPME
+#include <src/core/mapMe.h>
+#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 <netinet/in.h>
+#include <src/core/forwarder.h>
+#include <src/io/listener.h>
+#include <stdlib.h>
+
+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 <errno.h>
+#include <src/config.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <parc/assert/parc_Assert.h>
+#include <src/io/udpConnection.h>
+#include <src/io/udpListener.h>
+#include <src/io/udpTunnel.h>
+
+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 <src/core/forwarder.h>
+#include <src/io/ioOperations.h>
+#include <src/io/listener.h>
+#include <src/utils/address.h>
+
+/**
+ */
+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 <parc/algol/parc_ArrayList.h>
+#include <parc/algol/parc_Event.h>
+#include <parc/algol/parc_EventScheduler.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/config.h>
+#include <stdio.h>
+
+#include <src/messenger/messenger.h>
+#include <src/messenger/missiveDeque.h>
+
+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 <src/core/dispatcher.h>
+#include <src/messenger/messengerRecipient.h>
+#include <src/messenger/missive.h>
+
+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 <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/config.h>
+#include <stdio.h>
+
+#include <src/messenger/messenger.h>
+#include <src/messenger/messengerRecipient.h>
+
+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 <src/messenger/missive.h>
+
+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 <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Object.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/config.h>
+#include <stdio.h>
+
+#include <src/messenger/missive.h>
+
+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 <src/messenger/missiveType.h>
+
+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 <parc/algol/parc_Deque.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/config.h>
+#include <stdio.h>
+
+#include <src/messenger/missive.h>
+#include <src/messenger/missiveDeque.h>
+
+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 <errno.h>
+#include <src/config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+//#define __USE_MISC
+#include <net/if.h>
+
+// to get the list of arp types
+#include <net/if_arp.h>
+
+// for the mac address
+#include <netpacket/packet.h>
+
+#include <src/core/forwarder.h>
+#include <src/utils/interfaceSet.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#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 <errno.h>
+#include <ifaddrs.h>
+#include <src/config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <sys/socket.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <src/utils/interfaceSet.h>
+
+#include <src/core/forwarder.h>
+#include <src/core/system.h>
+
+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 <errno.h>
+#include <ifaddrs.h>
+#include <src/config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+//#define __USE_MISC
+#include <net/if.h>
+
+// to get the list of arp types
+#include <net/if_arp.h>
+
+// for the mac address
+#include <netpacket/packet.h>
+
+#include <src/core/forwarder.h>
+#include <src/utils/interfaceSet.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <src/utils/addressList.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/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 <src/config.h>
+#include <stdio.h>
+
+#include <src/processor/fib.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Network.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#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 <src/core/message.h>
+#include <src/core/name.h>
+#include <src/processor/fibEntry.h>
+#include <src/processor/fibEntryList.h>
+
+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 <src/config.h>
+#include <stdio.h>
+
+#include <src/core/numberSet.h>
+#include <src/processor/fibEntry.h>
+
+#include <src/core/nameBitvector.h>
+
+#include <src/strategies/loadBalancer.h>
+#include <src/strategies/loadBalancerWithPD.h>
+#include <src/strategies/rnd.h>
+#include <src/strategies/rndSegment.h>
+#include <src/strategies/strategyImpl.h>
+#ifdef WITH_MAPME
+#include <parc/algol/parc_HashMap.h>
+#include <src/core/ticks.h>
+#endif /* WITH_MAPME */
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+
+#include <src/utils/commands.h>
+
+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 <src/core/name.h>
+#include <src/strategies/strategyImpl.h>
+
+#ifdef WITH_MAPME
+#include <parc/algol/parc_EventTimer.h>
+#include <parc/algol/parc_Iterator.h>
+#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 <src/config.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <parc/algol/parc_ArrayList.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/processor/fibEntryList.h>
+
+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 <src/processor/fibEntry.h>
+
+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 <code>fibEntry</code>.
+ *
+ * @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 <src/config.h>
+#include <stdio.h>
+
+#include <parc/algol/parc_Hash.h>
+#include <parc/algol/parc_Memory.h>
+
+#include <src/core/message.h>
+#include <src/processor/hashTableFunction.h>
+
+#include <parc/assert/parc_Assert.h>
+
+// ======================================================================
+// 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 <parc/algol/parc_HashCodeTable.h>
+
+// ==========================================================
+// 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 <src/config.h>
+#include <stdio.h>
+
+#include <parc/algol/parc_Hash.h>
+#include <parc/algol/parc_Memory.h>
+
+#include <parc/assert/parc_Assert.h>
+#include <src/processor/hashTableFunction.h>
+#include <src/processor/matchingRulesTable.h>
+
+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
+ * <code>matchingRulesTable_AddToBestTable()</code> to add an interest to the
+ * "best" (i.e. most restrictive match) table, then calls
+ * <code>matchingRulesTable_GetUnion()</code> on a content object to match
+ * against all of them.
+ *
+ * When used in a ContentStore, one calls
+ * <code>matchingRulesTable_AddToAllTables()</code> to index a Content Object in
+ * all the tables. one then calls <code>matchingRulesTable_Get()</code> 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 <parc/algol/parc_ArrayList.h>
+#include <parc/algol/parc_HashCodeTable.h>
+#include <src/core/message.h>
+
+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 <src/config.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <parc/algol/parc_ArrayList.h>
+#include <parc/algol/parc_Memory.h>
+#include <src/processor/messageProcessor.h>
+
+#include <src/processor/fib.h>
+#include <src/processor/pitStandard.h>
+
+#include <src/content_store/contentStoreInterface.h>
+#include <src/content_store/contentStoreLRU.h>
+
+#include <src/strategies/loadBalancer.h>
+#include <src/strategies/loadBalancerWithPD.h>
+#include <src/strategies/rnd.h>
+#include <src/strategies/rndSegment.h>
+#include <src/strategies/strategyImpl.h>
+
+#include <src/io/streamConnection.h>
+#include <src/io/udpListener.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <src/utils/commands.h>
+#include <src/utils/utils.h>
+
+#include <src/utils/address.h>
+
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
+ * <code>messageProcessor_Receive()</code>, 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 <code>messageProcessor_ForwardToNexthops()</code>, 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
+ * <code>interestMessage</code> 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 <src/content_store/contentStoreInterface.h>
+#include <src/core/forwarder.h>
+#include <src/core/message.h>
+
+#include <src/utils/commands.h>
+
+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 <parc/assert/parc_Assert.h>
+#include <src/config.h>
+#include <stdio.h>
+
+#include <src/processor/pit.h>
+
+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 <src/core/forwarder.h>
+#include <src/core/message.h>
+#include <src/core/numberSet.h>
+#include <src/processor/pitEntry.h>
+#include <src/processor/pitVerdict.h>
+
+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
+ * <code>pitEntry_Destory()</code> 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 <src/config.h>
+#include <stdio.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <src/core/numberSet.h>
+#include <src/processor/pitEntry.h>
+
+#include <parc/assert/parc_Assert.h>
+
+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 <src/core/message.h>
+#include <src/core/numberSet.h>
+#include <src/core/ticks.h>
+#include <src/processor/fibEntry.h>
+
+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 <code>message_Release()</code>
+ * 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 <code>pitEntry_Release()</code> 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 <code>Message_Release()</code> on it.
+ *
+ * @return A reference counted copy, call <code>Message_Release()</code> 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 <src/config.h>
+#include <stdio.h>
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
+#include <src/processor/hashTableFunction.h>
+#include <src/processor/pit.h>
+
+#include <src/core/ticks.h>
+
+#include <parc/algol/parc_Hash.h>
+#include <parc/algol/parc_HashCodeTable.h>
+#include <parc/algol/parc_Memory.h>
+
+#include <src/core/forwarder.h>
+
+#include <parc/assert/parc_Assert.h>
+
+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 <src/processor/pit.h>
+
+/**
+ * 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 <arpa/inet.h> // inet_ntop
+#include <netdb.h> // ''
+#include <search.h> // tfind(), tdestroy(), twalk(), preorder...
+#include <stdbool.h>
+#include <stdio.h> // perror
+#include <stdlib.h> // calloc
+#include <string.h> // memcpy
+#include <sys/socket.h> // ''
+#include <sys/types.h> // getaddrinfo
+#include <unistd.h> // 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 <stdint.h> // uint*_t
+#include <stdlib.h>
+
+#include <hicn/hicn.h>
+#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 <hicn/hicn.h>
+#include <stdint.h>
+
+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 <sys/ioctl.h> // ioctl
+#include <sys/socket.h> // needed by linux/if.h
+//#include <linux/if.h>
+#include <errno.h>
+#include <fcntl.h> // ''
+#include <linux/if_tun.h>
+#include <linux/limits.h> // PATH_MAX
+#include <stdio.h> // fprintf
+#include <string.h> // memset
+#include <sys/stat.h> // open
+#include <sys/uio.h> // writev
+#include <unistd.h> // 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 <stdint.h>
+#include <stdlib.h>
+
+// 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 <arpa/inet.h> // inet_pton
+#include <errno.h> // errno
+#include <linux/fib_rules.h> // fib_rule_hdr, FRA_*
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <net/if.h> // IFF_UP
+#include <netinet/in.h> // in6addr
+#include <stdio.h> // perror
+#include <string.h>
+#include <sys/socket.h> // ''
+#include <sys/types.h> // socket
+#include <unistd.h> // read
+
+#include <sys/socket.h> // ''
+#include <sys/types.h> // 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 <net/if.h>
+// 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 <src/config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_HashMap.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Object.h>
+#include <parc/algol/parc_Unsigned.h>
+
+#include <src/strategies/loadBalancer.h>
+#include <src/strategies/nexthopState.h>
+
+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 <src/strategies/strategyImpl.h>
+
+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 <limits.h>
+#include <src/config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_HashMap.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Object.h>
+#include <parc/algol/parc_Unsigned.h>
+
+#include <src/strategies/loadBalancerWithPD.h>
+#include <src/strategies/nexthopStateWithPD.h>
+
+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 <src/core/connectionTable.h>
+#include <src/strategies/strategyImpl.h>
+
+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 <src/config.h>
+#include <stdio.h>
+
+#include <parc/algol/parc_DisplayIndented.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Object.h>
+
+#include <parc/assert/parc_Assert.h>
+#include <src/strategies/nexthopState.h>
+
+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 <parc/algol/parc_HashCode.h>
+#include <parc/algol/parc_Object.h>
+
+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 <float.h>
+#include <limits.h>
+#include <src/config.h>
+#include <stdio.h>
+
+#include <parc/algol/parc_DisplayIndented.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Object.h>
+#include <parc/assert/parc_Assert.h>
+
+#include <src/strategies/nexthopStateWithPD.h>
+
+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 <parc/algol/parc_HashCode.h>
+#include <parc/algol/parc_Object.h>
+
+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 <src/config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_HashMap.h>
+#include <parc/algol/parc_Memory.h>
+
+#include <src/strategies/rnd.h>
+
+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 <src/strategies/strategyImpl.h>
+
+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 <src/config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_HashMap.h>
+#include <parc/algol/parc_Memory.h>
+#include <src/core/nameBitvector.h>
+#include <src/strategies/rndSegment.h>
+
+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 <src/strategies/strategyImpl.h>
+
+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 <src/core/message.h>
+#include <src/core/numberSet.h>
+
+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 <arpa/inet.h>
+#include <errno.h>
+#include <src/config.h>
+#include <stdio.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <src/utils/address.h>
+
+#include <parc/algol/parc_Base64.h>
+#include <parc/algol/parc_BufferComposer.h>
+#include <parc/algol/parc_Hash.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Network.h>
+#include <parc/algol/parc_Object.h>
+
+#include <parc/assert/parc_Assert.h>
+
+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 <netinet/in.h>
+#include <stdbool.h>
+#include <sys/un.h>
+
+#include <parc/algol/parc_Buffer.h>
+#include <parc/algol/parc_BufferComposer.h>
+#include <src/utils/commands.h>
+
+/**
+ * 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(&copy);
+ * }
+ * @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(&copy);
+ * }
+ * @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 <src/config.h>
+#include <stdio.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <src/utils/addressList.h>
+
+#include <parc/algol/parc_ArrayList.h>
+#include <parc/algol/parc_Buffer.h>
+#include <parc/algol/parc_Memory.h>
+
+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 <src/utils/address.h>
+
+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 <netinet/in.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+
+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 <src/config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <parc/algol/parc_ArrayList.h>
+#include <parc/algol/parc_BufferComposer.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Object.h>
+#include <src/utils/addressList.h>
+#include <src/utils/interface.h>
+
+#include <parc/assert/parc_Assert.h>
+#include <src/utils/commands.h>
+
+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 <src/utils/address.h>
+#include <src/utils/addressList.h>
+
+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 <code>interfaceDestroy()</code>
+ *
+ * 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 <src/config.h>
+#include <stdio.h>
+
+#include <src/utils/interfaceSet.h>
+
+#include <parc/algol/parc_ArrayList.h>
+#include <parc/algol/parc_Memory.h>
+
+#include <parc/assert/parc_Assert.h>
+
+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 <src/utils/interface.h>
+
+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 .. <code>interfaceSetLength()-1</code>.
+ *
+ * @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 <code>interfaceEquals()</code>
+ *
+ * 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 <src/config.h>
+#include <stdio.h>
+
+#include <parc/assert/parc_Assert.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <src/utils/punting.h>
+
+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 <src/utils/address.h>
+
+/**
+ * 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 <ctype.h>
+#include <netinet/in.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Network.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/utils/utils.h>
+
+// 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 <src/config/controlState.h>
+#include <src/utils/address.h>
+#include <src/utils/commands.h>
+
+/**
+ * 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 <acompagn@cisco.com>
+ Luca Muscariello <lumuscar@cisco.com> \ 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=<vpp dir>/build-root/install-vpp-native/vpp/include/ -DHICN_INSTALL_PREFIX=<vpp src>/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=<vpp dir>/build-root/install-vpp_debug-native/vpp/include/ -DHICN_INSTALL_PREFIX=<vpp src>/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 <vpp install dir>/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 <vpp install dir>/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 <interface_name> 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 <vlib/vlib.h>
+#include <vppinfra/error.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+#include <vnet/udp/udp.h> // port registration
+#include <vnet/ip/ip6_packet.h> // ip46_address_t
+#include <vnet/ip/format.h>
+
+#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 <entries> | { dfltlife | minlife | maxlife } <seconds> } | fib size <entries> | cs {size <entries> | app <portion to reserved to 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 <prefix> face <faceid> }"
+ " | set strategy <strategy_id> prefix <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 <ip_address/mask> intfc <interface> type <ip/udp>",
+ .function = hicn_cli_punting_command_fn,
+};
+
+VLIB_CLI_COMMAND(hicn_cli_mapme_command, static)=
+{
+ .path = "hicn mapme",
+ .short_help = "hicn mapme {enable|disable|set <param> <value>}",
+ .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 <ip|hicn> src <addr> n_ifaces <n_ifaces> name <addr/subnet> lifetime <interest-lifetime> intfc <data in-interface> max_seq <max sequence number> n_flows <number of 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 <ip|hicn> name <addr/subnet> intfc <interest in-interface> size <payload_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 <vlib/buffer.h>
+
+#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 <vnet/dpo/dpo.h>
+
+#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 <vlib/vlib.h>
+#include <vnet/vnet.h>
+
+#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 <vnet/dpo/dpo.h>
+#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 <dlfcn.h>
+
+#include <vlib/vlib.h>
+#include <vnet/ip/ip6_packet.h>
+#include <vnet/ip/ip4.h> //ip4_add_del_ip_address
+#include <vnet/ip/ip6.h> //ip6_add_del_ip_address
+#include <vnet/fib/fib_types.h> //FIB_PROTOCOL_IP4/6, FIB_NODE_INDEX_INVALID
+#include <vnet/fib/fib_entry.h> //FIB_SOURCE_PLUGIN_HI
+#include <vnet/fib/fib_table.h>
+#include <vppinfra/format.h>
+#include <vnet/interface.h> //appif_flags
+#include <vnet/interface_funcs.h> //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 <vnet/vnet.h>
+#include <vnet/dpo/dpo.h>
+#include <vlib/vlib.h>
+#include <vnet/ip/ip6_packet.h>
+
+#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 <sw_if> { prod prefix <hicn_prefix> cs_size <size_in_packets>} {cons} | {del <face_id>}",
+ .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 <vnet/ip/ip6_packet.h>
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+
+#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 <vnet/vnet.h>
+#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 <vnet/ip/ip6_packet.h>
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+
+#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 <vnet/vnet.h>
+#include <vlib/vlib.h>
+#include <vnet/dpo/dpo.h>
+#include <vnet/adj/adj_types.h>
+
+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 <vlib/vlib.h>
+#include <vppinfra/error.h>
+#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 [<face_id>| type <ip/udp>]",
+ .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 <vnet/vnet.h>
+#include <vnet/ip/ip4_packet.h>
+
+#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 <vlib/vlib.h>
+#include <vnet/vnet.h>
+#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 <vnet/vnet.h>
+#include <vnet/dpo/dpo.h>
+#include <vlib/vlib.h>
+
+#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 <local_address> remote <remote_address> intfc <sw_if>} {app_face <0/1>} {cs_size <size_in_packets>} | {del id <face_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 <vnet/adj/adj.h>
+
+#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 <vlib/vlib.h>
+#include <vnet/vnet.h>
+
+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 <hicn/hicn.h>
+#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 <vlib/vlib.h>
+#include <vnet/vnet.h>
+
+/**
+ * @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 <vnet/ip/format.h>
+#include <vnet/adj/adj.h>
+#include <vnet/vnet.h>
+#include <vlib/vlib.h>
+
+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 <vnet/adj/adj_types.h>
+#include <vnet/ip/ip4_packet.h>
+#include <vnet/ip/ip6_packet.h>
+
+#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 <vlib/vlib.h>
+#include <vnet/vnet.h>
+
+#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 <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vnet/ip/ip6_packet.h>
+#include <vnet/udp/udp_packet.h>
+
+#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 <vnet/vnet.h>
+#include <vnet/dpo/dpo.h>
+#include <vlib/vlib.h>
+#include <vnet/ip/format.h>
+
+#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 <src_address> port <src_port > dst_addr <dst_address> port <dst_port>} intfc <interface> | {del id <face_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 <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vnet/ip/ip_packet.h>
+
+#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 <vlib/vlib.h>
+#include <vnet/vnet.h>
+
+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 <vlib/vlib.h>
+
+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 <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <inttypes.h>
+
+#include <vlib/vlib.h>
+#include <vppinfra/pool.h>
+
+#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 <stdint.h>
+#include <vppinfra/bihash_8_8.h>
+#include <vppinfra/bihash_24_8.h>
+
+#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 <vnet/vnet.h>
+#include <vnet/plugin/plugin.h>
+
+#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 <hicn/hicn.h>
+
+#include <netinet/in.h>
+#include <vnet/ip/ip.h>
+#include <vnet/tcp/tcp_packet.h>
+#include <vnet/ip/ip6_packet.h>
+#include <vnet/ip/ip4_packet.h>
+#include <vnet/buffer.h>
+
+/* 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 <hicn/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.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 <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vppinfra/error.h>
+#include <vnet/ip/format.h>
+#include <vnet/ip/ip4.h>
+#include <vnet/ip/ip6.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+#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 <hicn/hicn_msg_enum.h>
+
+/* define generated endian-swappers */
+#define vl_endianfun
+#include <hicn/hicn_all_api_h.h>
+#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 <hicn/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/hicn_all_api_h.h>
+#undef vl_api_version
+
+#define REPLY_MSG_ID_BASE sm->msg_id_base
+#include <vlibapi/api_helper_macros.h>
+
+/****** 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 <hicn/hicn_all_api_h.h>
+#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 <inttypes.h>
+
+#include <vat/vat.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vppinfra/error.h>
+
+#include <vnet/ip/ip4_packet.h>
+#include <vnet/ip/ip6_packet.h>
+#include <vnet/ip/format.h>
+
+#define __plugin_msg_base hicn_test_main.msg_id_base
+#include <vlibapi/vat_helper_macros.h>
+
+
+#include <hicn/hicn_api.h>
+#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 <sz> CS size <sz>" \
+ "PIT minlimit <f> PIT maxlimit <f> [disable] ") \
+_(hicn_api_node_params_get, "") \
+_(hicn_api_node_stats_get, "") \
+_(hicn_api_face_ip_del, "face <faceID>") \
+_(hicn_api_face_ip_add, "add <swif> <address>") \
+_(hicn_api_route_nhops_add, "add prefix <IP4/IP6>/<subnet> face <faceID> weight <weight>") \
+_(hicn_api_face_ip_params_get, "face <faceID>") \
+_(hicn_api_route_get, "prefix <IP4/IP6>/<subnet>") \
+_(hicn_api_route_del, "prefix <IP4/IP6>/<subnet>") \
+_(hicn_api_route_nhop_del, "del prefix <IP4/IP6>/<subnet> face <faceID>") \
+_(hicn_api_strategies_get, "") \
+_(hicn_api_strategy_get, "strategy <id>") \
+_(hicn_api_register_prod_app, "prefix <IP4/IP6>/<subnet> id <appif_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 <vppinfra/byte_order.h>
+
+#define vl_msg_id(n, h) n,
+typedef enum
+{
+#include <hicn/hicn_all_api_h.h>
+ /* 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 <vlib/vlib.h>
+#include <vnet/vnet.h>
+
+#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 <vlib/vlib.h>
+#include <vnet/vnet.h>
+
+#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 <vnet/ip/ip6_packet.h>
+#include <vppinfra/string.h>
+
+#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 <vlib/vlib.h>
+#include <vnet/vnet.h>
+
+#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 <vnet/ip/ip6_packet.h>
+
+#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 <vlib/vlib.h>
+#include <vnet/vnet.h>
+
+#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 <vnet/ip/ip6_packet.h>
+
+#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 <vnet/dpo/load_balance.h>
+#include <vnet/buffer.h>
+#include <hicn/hicn.h>
+#include <hicn/mapme.h>
+
+#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 <vlib/vlib.h>
+#include <vnet/vnet.h>
+
+/* 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 <vnet/ip/ip6_packet.h>
+#include <hicn/hicn.h>
+
+#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, &params);
+ 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 <vlib/vlib.h>
+#include <vnet/vnet.h>
+
+/* 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 <vnet/ip/ip6_packet.h>
+#include <vnet/dpo/load_balance.h>
+#include <hicn/hicn.h>
+
+#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, &params);
+ 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), &params);
+
+ 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 <vnet/fib/ip4_fib.h>
+#include <vnet/fib/ip6_fib.h>
+
+#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, &params,
+ &tfib->next_hops[pos]);
+ }
+ }
+ else
+ {
+ hicn_mapme_send_message (vm, prefix, &params,
+ &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/vlib.h> // 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 <vlib/vlib.h>
+#include <vppinfra/error.h>
+
+#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 <vppinfra/error.h>
+#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 <vlib/vlib.h>
+
+#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 <stdlib.h>
+#include <vlib/vlib.h>
+
+#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 <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vnet/pg/pg.h>
+#include <vnet/ip/ip.h>
+#include <vnet/ethernet/ethernet.h>
+
+#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 <stdarg.h>
+#include <stddef.h> // offsetof()
+#include <inttypes.h>
+#include <vlib/vlib.h>
+#include <vppinfra/error.h>
+#include <vnet/ip/format.h>
+#include <vnet/classify/in_out_acl.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vnet/ip/ip4_packet.h>
+#include <vnet/ip/ip6_packet.h>
+#include <vnet/ethernet/packet.h>
+#include <vlib/global_funcs.h>
+#include <hicn/hicn.h>
+
+#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 <vppinfra/error.h>
+#include <hicn/hicn.h>
+
+#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 <vnet/fib/fib_entry.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/ip/ip6_packet.h>
+#include <vnet/dpo/dpo.h>
+#include <vnet/dpo/load_balance.h>
+#include <vlib/global_funcs.h>
+
+#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 <vlib/vlib.h>
+#include <vppinfra/error.h>
+#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 <netinet/in.h>
+#include <vnet/buffer.h>
+
+#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 <vnet/dpo/dpo.h>
+#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 <vlib/vlib.h>
+#include <vnet/vnet.h>
+
+#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 <vnet/vnet.h>
+#include <vnet/dpo/dpo.h>
+#include <vlib/vlib.h>
+#include <vnet/fib/fib_entry.h>
+#include <vnet/fib/fib_table.h>
+
+#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 <prefix> face <face_id> weight <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 <vlib/vlib.h>
+#include <vnet/vnet.h>
+
+#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 <vnet/dpo/dpo.h>
+#include <vnet/fib/fib_table.h>
+
+#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 <vnet/dpo/dpo.h>
+
+#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 <vnet/dpo/dpo.h>
+#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 <stdlib.h>
+#include <string.h> // memset
+#include <sys/types.h> // getaddrinfo
+#include <sys/socket.h> // ''
+#include <netdb.h> // ''
+#include <stdio.h>
+
+#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 <stdint.h>
+#include <assert.h>
+
+/* 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 <vnet/ip/ip4_packet.h> // ip4_address_t
+#include <vnet/ip/ip6_packet.h> // ip6_address_t
+
+#else
+
+#include <netinet/in.h>
+
+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 <netinet/in.h>
+#include <string.h> // memset
+#include <stddef.h> // 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 <hicn/header.h>
+#include <hicn/name.h>
+#include <hicn/ops.h>
+#include <hicn/mapme.h>
+
+#else
+
+#include <hicn/error.h>
+#include <hicn/header.h>
+#include <hicn/name.h>
+#include <hicn/ops.h>
+#include <hicn/mapme.h>
+#include <hicn/compat.h>
+
+#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 <stdint.h> // u32
+#include <stdbool.h>
+
+#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 <arpa/inet.h> // inet_ptin
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h> // strtoul
+#include <string.h> // 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 <stdbool.h>
+#include <netinet/in.h> // 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 <netinet/in.h>
+#include <stdlib.h>
+#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 <stdlib.h>
+
+#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 <string.h> // 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 <string.h>
+#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 <arpa/inet.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 <stdlib.h>
+#include <string.h>
+
+#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 <string.h>
+#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 <msardara@cisco.com>
+ Michele Papalini <micpapal@cisco.com>
+ Alberto Compagno <acompagn@cisco.com>
+ Luca Muscariello <lumuscar@cisco.com>
+
+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 <hicn/transport/core/connector.h>
+
+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<uint8_t *>(malloc(capacity));
+ std::unique_ptr<utils::MemBuf> 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 <hicn/transport/core/packet.h>
+#include <hicn/transport/utils/membuf.h>
+#include <hicn/transport/utils/object_pool.h>
+#include <hicn/transport/utils/ring_buffer.h>
+
+#include <deque>
+#include <functional>
+
+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<Packet::MemBufPtr, queue_size>;
+using PacketQueue = std::deque<Packet::MemBufPtr>;
+using PacketReceivedCallback = std::function<void(Packet::MemBufPtr &&)>;
+using OnReconnect = std::function<void()>;
+using PacketSentCallback = std::function<void()>;
+
+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<utils::MemBuf>::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<utils::MemBuf> 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 <hicn/transport/core/content_object.h>
+#include <hicn/transport/errors/errors.h>
+#include <hicn/transport/utils/branch_prediction.h>
+
+extern "C" {
+TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat")
+#include <hicn/hicn.h>
+}
+
+#include <cstring>
+#include <memory>
+
+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<hicn_header_t *>(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 <hicn/transport/core/name.h>
+#include <hicn/transport/core/packet.h>
+
+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<ContentObject>::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 <hicn/transport/core/forwarder_interface.h>
+#include <hicn/transport/core/hicn_forwarder_interface.h>
+#include <hicn/transport/core/manifest_format_fixed.h>
+#include <hicn/transport/core/manifest_inline.h>
+#include <hicn/transport/core/portal.h>
+
+#ifdef __linux__
+#ifndef __ANDROID__
+#include <hicn/transport/core/raw_socket_interface.h>
+#ifdef __vpp__
+#include <hicn/transport/core/vpp_forwarder_interface.h>
+#endif
+#endif
+#endif
+
+namespace transport {
+
+namespace core {
+
+using HicnForwarderPortal = Portal<HicnForwarderInterface>;
+
+#ifdef __linux__
+#ifndef __ANDROID_API__
+using RawSocketPortal = Portal<RawSocketInterface>;
+#endif
+#ifdef __vpp__
+using VPPForwarderPortal = Portal<VPPForwarderInterface>;
+#endif
+#endif
+
+using ContentObjectManifest = core::ManifestInline<ContentObject, Fixed>;
+using InterestManifest = core::ManifestInline<Interest, Fixed>;
+
+} // 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 <hicn/transport/core/prefix.h>
+#include <hicn/transport/core/socket_connector.h>
+#include <hicn/transport/portability/portability.h>
+
+#include <deque>
+
+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 <typename Implementation, typename ConnectorType>
+class ForwarderInterface {
+ static_assert(std::is_base_of<Connector, ConnectorType>::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<Implementation &>(*this).connect(is_consumer);
+ }
+
+ TRANSPORT_ALWAYS_INLINE void registerRoute(Prefix &prefix) {
+ static_cast<Implementation &>(*this).registerRoute();
+ }
+
+ TRANSPORT_ALWAYS_INLINE std::uint32_t getMtu() {
+ return static_cast<Implementation &>(*this).getMtu();
+ }
+
+ template <
+ typename R,
+ typename = std::enable_if_t<
+ std::is_base_of<Packet, typename std::remove_reference_t<R>>::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 <typename Handler>
+ 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<Handler>(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 <hicn/transport/config.h>
+
+#ifdef __vpp__
+
+#include <hicn/transport/core/hicn_binary_api.h>
+#include <hicn/transport/core/vpp_binary_api_internal.h>
+
+#include <hicn/transport/core/hicn_binary_api.h>
+#include <hicn/transport/core/vpp_binary_api_internal.h>
+#include <hicn/transport/utils/log.h>
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <semaphore.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <vlib/vlib.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vppinfra/error.h>
+
+#include <vnet/ip/format.h>
+#include <vnet/ip/ip4_packet.h>
+#include <vnet/ip/ip6_packet.h>
+
+#include <vpp_plugins/hicn/error.h>
+#include <vpp_plugins/hicn/hicn_api.h>
+
+// uword unformat_sw_if_index (unformat_input_t * input, va_list * args);
+
+/* Declare message IDs */
+#include <hicn/hicn_msg_enum.h>
+
+#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 <hicn/hicn_all_api_h.h>
+#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 <hicn/transport/config.h>
+
+#ifdef __vpp__
+
+#include <hicn/transport/core/vpp_binary_api.h>
+
+#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 <hicn/transport/core/hicn_forwarder_interface.h>
+
+#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<HicnForwarderInterface, SocketConnector>(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 <hicn/transport/core/forwarder_interface.h>
+#include <hicn/transport/core/prefix.h>
+#include <hicn/transport/core/socket_connector.h>
+
+#include <deque>
+
+namespace transport {
+
+namespace core {
+
+class HicnForwarderInterface
+ : public ForwarderInterface<HicnForwarderInterface, SocketConnector> {
+ 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
--- /dev/null
+++ b/libtransport/src/hicn/transport/core/hicn_memif_api.c
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 <hicn/transport/core/interest.h>
+#include <hicn/transport/errors/errors.h>
+#include <hicn/transport/utils/hash.h>
+
+extern "C" {
+TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat")
+#include <hicn/hicn.h>
+}
+
+#include <cstring>
+#include <memory>
+
+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<hicn_header_t *>(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 <hicn/transport/core/name.h>
+#include <hicn/transport/core/packet.h>
+#include <hicn/transport/utils/object_pool.h>
+
+namespace transport {
+
+namespace core {
+
+class Interest
+ : public Packet /*, public std::enable_shared_from_this<Interest>*/ {
+ public:
+ using Ptr = utils::ObjectPool<Interest>::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 <hicn/transport/core/key_locator.h>
+
+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 <hicn/transport/core/key_locator_type.h>
+#include <hicn/transport/core/name.h>
+
+namespace transport {
+
+namespace core {
+
+class KeyLocator : public std::enable_shared_from_this<KeyLocator> {
+ 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 <hicn/transport/core/manifest.h>
+
+namespace transport {
+
+namespace core {
+
+std::string ManifestEncoding::manifest_type = std::string("manifest_type");
+
+std::map<ManifestType, std::string> 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 <hicn/transport/core/content_object.h>
+#include <hicn/transport/core/manifest_format.h>
+#include <hicn/transport/core/name.h>
+
+#include <set>
+
+namespace transport {
+
+namespace core {
+
+using typename core::Name;
+using typename core::Packet;
+using typename core::PayloadType;
+
+template <typename Base, typename FormatTraits, typename ManifestImpl>
+class Manifest : public Base {
+ static_assert(std::is_base_of<Packet, Base>::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 <typename T>
+ Manifest(T&& base)
+ : packet_(new Base(std::move<T&&>(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<ManifestImpl&>(*this).estimateManifestSizeImpl(
+ additional_entries);
+ }
+
+ /*
+ * After the call to encode, users MUST call clear before adding data
+ * to the manifest.
+ */
+ Manifest& encode() { return static_cast<ManifestImpl&>(*this).encodeImpl(); }
+
+ Manifest& decode() {
+ Manifest::decoder_.decode();
+
+ manifest_type_ = decoder_.getManifestType();
+ hash_algorithm_ = decoder_.getHashAlgorithm();
+ is_last_ = decoder_.getIsFinalManifest();
+
+ return static_cast<ManifestImpl&>(*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 <hicn/transport/core/name.h>
+#include <hicn/transport/utils/crypto_hasher.h>
+
+#include <cinttypes>
+#include <type_traits>
+#include <unordered_map>
+
+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<uint8_t>(utils::CryptoHashType::SHA_256),
+ SHA_512 = static_cast<uint8_t>(utils::CryptoHashType::SHA_512),
+ CRC32C = static_cast<uint8_t>(utils::CryptoHashType::CRC32C),
+};
+
+enum class NextSegmentCalculationStrategy : uint8_t {
+ INCREMENTAL = 1,
+};
+
+template <typename T>
+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 <typename Implementation>
+class ManifestEncoder {
+ public:
+ virtual ~ManifestEncoder() = default;
+
+ ManifestEncoder encode() {
+ return static_cast<Implementation&>(*this).encodeImpl();
+ }
+
+ ManifestEncoder& clear() {
+ return static_cast<Implementation&>(*this).clearImpl();
+ }
+
+ ManifestEncoder& setManifestType(ManifestType type) {
+ return static_cast<Implementation&>(*this).setManifestTypeImpl(type);
+ }
+
+ ManifestEncoder& setHashAlgorithm(HashAlgorithm hash) {
+ return static_cast<Implementation&>(*this).setHashAlgorithmImpl(hash);
+ }
+
+ ManifestEncoder& setFinalChunkNumber(uint32_t final_chunk) {
+ return static_cast<Implementation&>(*this).setFinalChunkImpl(final_chunk);
+ }
+
+ ManifestEncoder& setNextSegmentCalculationStrategy(
+ NextSegmentCalculationStrategy strategy) {
+ return static_cast<Implementation&>(*this)
+ .setNextSegmentCalculationStrategyImpl(strategy);
+ }
+
+ template <
+ typename T,
+ typename = std::enable_if_t<std::is_same<
+ std::remove_const_t<std::remove_reference_t<T>>, core::Name>::value>>
+ ManifestEncoder& setBaseName(T&& name) {
+ return static_cast<Implementation&>(*this).setBaseNameImpl(name);
+ }
+
+ template <typename Hash>
+ ManifestEncoder& addSuffixAndHash(uint32_t suffix, Hash&& hash) {
+ return static_cast<Implementation&>(*this).addSuffixAndHashImpl(
+ suffix, std::forward<Hash&&>(hash));
+ }
+
+ ManifestEncoder& setIsFinalManifest(bool is_last) {
+ return static_cast<Implementation&>(*this).setIsFinalManifestImpl(is_last);
+ }
+
+ ManifestEncoder& setVersion(ManifestVersion version) {
+ return static_cast<Implementation&>(*this).setVersionImpl(version);
+ }
+
+ std::size_t estimateSerializedLength(std::size_t number_of_entries) {
+ return static_cast<Implementation&>(*this).estimateSerializedLengthImpl(
+ number_of_entries);
+ }
+
+ ManifestEncoder& update() {
+ return static_cast<Implementation&>(*this).updateImpl();
+ }
+
+ ManifestEncoder& setFinalBlockNumber(std::uint32_t final_block_number) {
+ return static_cast<Implementation&>(*this).setFinalBlockNumberImpl(
+ final_block_number);
+ }
+
+ static std::size_t getManifestHeaderSize() {
+ return Implementation::getManifestHeaderSizeImpl();
+ }
+};
+
+template <typename Implementation>
+class ManifestDecoder {
+ public:
+ virtual ~ManifestDecoder() = default;
+
+ ManifestDecoder& clear() {
+ return static_cast<Implementation&>(*this).clearImpl();
+ }
+
+ void decode() { static_cast<Implementation&>(*this).decodeImpl(); }
+
+ ManifestType getManifestType() const {
+ return static_cast<const Implementation&>(*this).getManifestTypeImpl();
+ }
+
+ HashAlgorithm getHashAlgorithm() const {
+ return static_cast<const Implementation&>(*this).getHashAlgorithmImpl();
+ }
+
+ uint32_t getFinalChunkNumber() const {
+ return static_cast<const Implementation&>(*this).getFinalChunkImpl();
+ }
+
+ NextSegmentCalculationStrategy getNextSegmentCalculationStrategy() const {
+ return static_cast<const Implementation&>(*this)
+ .getNextSegmentCalculationStrategyImpl();
+ }
+
+ core::Name getBaseName() const {
+ return static_cast<const Implementation&>(*this).getBaseNameImpl();
+ }
+
+ auto getSuffixHashList() {
+ return static_cast<Implementation&>(*this).getSuffixHashListImpl();
+ }
+
+ bool getIsFinalManifest() const {
+ return static_cast<const Implementation&>(*this).getIsFinalManifestImpl();
+ }
+
+ ManifestVersion getVersion() const {
+ return static_cast<const Implementation&>(*this).getVersionImpl();
+ }
+
+ std::size_t estimateSerializedLength(std::size_t number_of_entries) const {
+ return static_cast<const Implementation&>(*this)
+ .estimateSerializedLengthImpl(number_of_entries);
+ }
+
+ uint32_t getFinalBlockNumber() const {
+ return static_cast<const Implementation&>(*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 <hicn/transport/core/manifest_format_fixed.h>
+#include <hicn/transport/core/packet.h>
+#include <hicn/transport/utils/endianess.h>
+#include <hicn/transport/utils/literals.h>
+
+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<ManifestHeader*>(manifest_->writableData())),
+ manifest_entries_(reinterpret_cast<ManifestEntry*>(
+ 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<uint8_t>(algorithm);
+ return *this;
+}
+
+FixedManifestEncoder& FixedManifestEncoder::setManifestTypeImpl(
+ ManifestType manifest_type) {
+ manifest_header_->manifest_type = static_cast<uint8_t>(manifest_type);
+ return *this;
+}
+
+FixedManifestEncoder&
+FixedManifestEncoder::setNextSegmentCalculationStrategyImpl(
+ NextSegmentCalculationStrategy strategy) {
+ manifest_header_->next_segment_strategy = static_cast<uint8_t>(strategy);
+ return *this;
+}
+
+FixedManifestEncoder& FixedManifestEncoder::setBaseNameImpl(
+ const core::Name& base_name) {
+ base_name.copyToDestination(
+ reinterpret_cast<uint8_t*>(&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<std::uint8_t>();
+ 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<uint8_t*>(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<uint8_t>(is_last);
+ return *this;
+}
+
+FixedManifestEncoder& FixedManifestEncoder::setVersionImpl(
+ ManifestVersion version) {
+ manifest_header_->version = static_cast<uint8_t>(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<ManifestHeader*>(
+ const_cast<uint8_t*>(packet_.getPayload().data()));
+ manifest_entries_ = reinterpret_cast<ManifestEntry*>(
+ const_cast<uint8_t*>(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<ManifestHeader*>(
+ const_cast<uint8_t*>(packet_.getPayload().data()))),
+ manifest_entries_(reinterpret_cast<ManifestEntry*>(
+ const_cast<uint8_t*>(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<ManifestType>(manifest_header_->manifest_type);
+}
+
+HashAlgorithm FixedManifestDecoder::getHashAlgorithmImpl() const {
+ return static_cast<HashAlgorithm>(manifest_header_->hash_algorithm);
+}
+
+NextSegmentCalculationStrategy
+FixedManifestDecoder::getNextSegmentCalculationStrategyImpl() const {
+ return static_cast<NextSegmentCalculationStrategy>(
+ 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<uint8_t*>(
+ &manifest_entries_[i].hash[0])));
+ }
+
+ return hash_list;
+}
+
+core::Name FixedManifestDecoder::getBaseNameImpl() const {
+ if (static_cast<bool>(manifest_header_->flags.ipv6)) {
+ return core::Name(AF_INET6,
+ reinterpret_cast<uint8_t*>(&manifest_header_->prefix));
+ } else {
+ return core::Name(AF_INET,
+ reinterpret_cast<uint8_t*>(&manifest_header_->prefix));
+ }
+}
+
+bool FixedManifestDecoder::getIsFinalManifestImpl() const {
+ return static_cast<bool>(manifest_header_->flags.is_last);
+}
+
+ManifestVersion FixedManifestDecoder::getVersionImpl() const {
+ return static_cast<ManifestVersion>(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 <hicn/transport/core/manifest_format.h>
+#include <hicn/transport/core/packet.h>
+
+#include <string>
+
+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<std::pair<std::uint32_t, std::uint8_t*>>;
+};
+
+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<FixedManifestEncoder> {
+ 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<utils::MemBuf> manifest_;
+ ManifestHeader* manifest_header_;
+ ManifestEntry* manifest_entries_;
+ std::size_t current_entry_;
+};
+
+class FixedManifestDecoder : public ManifestDecoder<FixedManifestDecoder> {
+ 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 <hicn/transport/core/packet.h>
+#include <hicn/transport/portability/transport_portability.h>
+
+#include <array>
+
+namespace transport {
+
+namespace core {
+
+namespace {
+
+template <typename T>
+TRANSPORT_ALWAYS_INLINE void checkPointer(T* pointer) {
+ if (pointer == nullptr) {
+ throw errors::NullPointerException();
+ }
+}
+
+template <typename EnumType>
+TRANSPORT_ALWAYS_INLINE void setValueToJson(Json::Value& root, EnumType value) {
+ root[JSONKey<EnumType>::key] = static_cast<uint8_t>(value);
+}
+
+template <typename EnumType>
+TRANSPORT_ALWAYS_INLINE EnumType getValueFromJson(const Json::Value& root) {
+ return static_cast<EnumType>(root[JSONKey<EnumType>::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<const uint8_t*>(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<core::Name>::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<SuffixHashList>::key].append(value);
+
+ return *this;
+}
+
+TRANSPORT_ALWAYS_INLINE JSONManifestEncoder&
+JSONManifestEncoder::setIsFinalManifestImpl(bool is_last) {
+ root_[JSONKey<bool>::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<ManifestType>(root_);
+}
+
+TRANSPORT_ALWAYS_INLINE HashAlgorithm
+JSONManifestDecoder::getHashAlgorithmImpl() const {
+ return getValueFromJson<HashAlgorithm>(root_);
+}
+
+TRANSPORT_ALWAYS_INLINE NextSegmentCalculationStrategy
+JSONManifestDecoder::getNextSegmentCalculationStrategyImpl() const {
+ return getValueFromJson<NextSegmentCalculationStrategy>(root_);
+}
+
+TRANSPORT_ALWAYS_INLINE typename JSON::SuffixList
+JSONManifestDecoder::getSuffixHashListImpl() {
+ throw errors::NotImplementedException();
+ // SuffixHashList hash_list;
+ //
+ // Json::Value &array = root_[JSONKey<SuffixHashList>::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<core::Name>::key].asCString());
+}
+
+TRANSPORT_ALWAYS_INLINE bool JSONManifestDecoder::getIsFinalManifestImpl()
+ const {
+ return root_[JSONKey<bool>::final_manifest].asBool();
+}
+
+TRANSPORT_ALWAYS_INLINE ManifestVersion
+JSONManifestDecoder::getVersionImpl() const {
+ return getValueFromJson<ManifestVersion>(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 <hicn/transport/core/manifest_format.h>
+#include <hicn/transport/core/name.h>
+
+#if defined(__APPLE__) || defined(__ANDROID__)
+#include <json/json.h>
+#else
+#include <jsoncpp/json/json.h>
+#endif /* __APPLE__ || __ANDROID__*/
+
+#include <string>
+
+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<std::uint32_t, std::uint8_t*>;
+};
+
+template <typename T>
+struct JSONKey;
+
+template <>
+struct JSONKey<ManifestVersion> {
+ static const constexpr char* key = "manifest_version";
+};
+
+template <>
+struct JSONKey<HashAlgorithm> {
+ static const constexpr char* key = "hash_algorithm";
+};
+
+template <>
+struct JSONKey<ManifestType> {
+ static const constexpr char* key = "manifest_type";
+};
+
+template <>
+struct JSONKey<NextSegmentCalculationStrategy> {
+ static const constexpr char* key = "next_segment_strategy";
+};
+
+template <>
+struct JSONKey<typename JSON::SuffixList> {
+ static const constexpr char* key = "suffix_hash_list";
+};
+
+template <>
+struct JSONKey<core::Name> {
+ static const constexpr char* key = "base_name";
+};
+
+template <>
+struct JSONKey<bool> {
+ static const constexpr char* final_manifest = "final_manifest";
+};
+
+class JSONManifestEncoder : public ManifestEncoder<JSONManifestEncoder> {
+ 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<JSONManifestDecoder> {
+ 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 <hicn/transport/core/manifest_format_json_libparc_deprecated.h>
+#include <hicn/transport/core/packet.h>
+#include <hicn/transport/errors/errors.h>
+#include <hicn/transport/portability/transport_portability.h>
+
+extern "C" {
+#include <parc/algol/parc_Memory.h>
+}
+
+namespace transport {
+
+namespace core {
+
+namespace {
+
+template <typename T>
+TRANSPORT_ALWAYS_INLINE void checkPointer(T* pointer) {
+ if (pointer == nullptr) {
+ throw errors::NullPointerException();
+ }
+}
+
+template <typename EnumType>
+TRANSPORT_ALWAYS_INLINE void setValueToJson(PARCJSON* root, EnumType value) {
+ parcJSON_AddInteger(root, JSONKey<EnumType>::key,
+ static_cast<int64_t>(value));
+}
+
+template <typename EnumType>
+TRANSPORT_ALWAYS_INLINE EnumType getValueFromJson(PARCJSON* root) {
+ checkPointer(root);
+
+ PARCJSONValue* value = parcJSON_GetValueByName(root, JSONKey<EnumType>::key);
+
+ EnumType ret = static_cast<EnumType>(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<uint8_t*>(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<core::Name>::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<SuffixHashList>::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<SuffixHashList>::key,
+ // array);
+ // parcJSONArray_Release(&array);
+ //
+ // value = parcJSON_GetValueByName(root_, JSONKey<SuffixHashList>::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<bool>::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<uint8_t*>(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<ManifestType>(root_);
+}
+
+TRANSPORT_ALWAYS_INLINE HashAlgorithm
+JSONManifestDecoder::getHashAlgorithmImpl() const {
+ return getValueFromJson<HashAlgorithm>(root_);
+}
+
+TRANSPORT_ALWAYS_INLINE NextSegmentCalculationStrategy
+JSONManifestDecoder::getNextSegmentCalculationStrategyImpl() const {
+ return getValueFromJson<NextSegmentCalculationStrategy>(root_);
+}
+
+TRANSPORT_ALWAYS_INLINE SuffixHashList
+JSONManifestDecoder::getSuffixHashListImpl() {
+ throw errors::NotImplementedException();
+ // SuffixHashList hash_list;
+ //
+ // char * str = parcJSON_ToString(root_);
+ //
+ // PARCJSONValue *value = parcJSON_GetValueByName(root_,
+ // JSONKey<SuffixHashList>::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<uint32_t>(parcJSONValue_GetInteger(_suffix)); uint64_t
+ // value2 = static_cast<uint64_t>(parcJSONValue_GetInteger(_hash));
+ //
+ // hash_list[static_cast<uint32_t>(parcJSONValue_GetInteger(_suffix))] =
+ // static_cast<uint64_t>(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<core::Name>::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<bool>::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 <hicn/transport/core/manifest_format.h>
+#include <hicn/transport/core/name.h>
+
+extern "C" {
+#include <parc/algol/parc_JSON.h>
+}
+
+#include <string>
+
+namespace transport {
+
+namespace core {
+
+class JSONManifestEncoder;
+class JSONManifestDecoder;
+class Packet;
+
+struct JSON {
+ using Encoder = JSONManifestEncoder;
+ using Decoder = JSONManifestDecoder;
+};
+
+template <typename T>
+struct JSONKey;
+
+template <>
+struct JSONKey<HashAlgorithm> {
+ static const constexpr char* key = "hash_algorithm";
+};
+
+template <>
+struct JSONKey<ManifestType> {
+ static const constexpr char* key = "manifest_type";
+};
+
+template <>
+struct JSONKey<NextSegmentCalculationStrategy> {
+ static const constexpr char* key = "next_segment_strategy";
+};
+
+template <>
+struct JSONKey<NameHashList> {
+ static const constexpr char* key = "name_hash_list";
+};
+
+template <>
+struct JSONKey<SuffixHashList> {
+ static const constexpr char* key = "suffix_hash_list";
+};
+
+template <>
+struct JSONKey<core::Name> {
+ static const constexpr char* key = "base_name";
+};
+
+template <>
+struct JSONKey<bool> {
+ static const constexpr char* final_manifest = "final_manifest";
+};
+
+// template <>
+// struct JSONKey<base_name> {
+// 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<JSONManifestEncoder> {
+ 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<JSONManifestDecoder> {
+ 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 <hicn/transport/core/manifest.h>
+#include <hicn/transport/core/manifest_format.h>
+#include <hicn/transport/portability/portability.h>
+#include <hicn/transport/utils/log.h>
+
+#include <set>
+
+namespace transport {
+
+namespace core {
+
+template <typename Base, typename FormatTraits>
+class ManifestInline
+ : public Manifest<Base, FormatTraits, ManifestInline<Base, FormatTraits>> {
+ using ManifestBase =
+ Manifest<Base, FormatTraits, ManifestInline<Base, FormatTraits>>;
+ using HashType = typename FormatTraits::HashType;
+ using SuffixList = typename FormatTraits::SuffixList;
+
+ public:
+ ManifestInline() : ManifestBase() {}
+
+ ManifestInline(const core::Name& name) : ManifestBase(name) {}
+
+ template <typename T>
+ ManifestInline(T&& base) : ManifestBase(std::forward<T&&>(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 <hicn/transport/config.h>
+
+#ifdef __vpp__
+
+#include <hicn/transport/core/memif_binary_api.h>
+#include <hicn/transport/core/vpp_binary_api_internal.h>
+#include <hicn/transport/utils/log.h>
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <semaphore.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+// uword unformat_sw_if_index (unformat_input_t * input, va_list * args);
+
+/* Declare message IDs */
+#include <memif/memif_msg_enum.h>
+
+#define vl_msg_name_crc_list
+#include <vpp/api/vpe_all_api_h.h>
+#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 <memif/memif_all_api_h.h>
+#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 <vpp/api/vpe_all_api_h.h>
+#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 <hicn/transport/config.h>
+#include <hicn/transport/core/vpp_binary_api.h>
+
+#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 <hicn/transport/core/memif_connector.h>
+
+#ifdef __vpp__
+
+#include <sys/epoll.h>
+#include <cstdlib>
+
+#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<utils::FdDeadlineTimer>(event_reactor_)),
+ io_service_(io_service),
+ work_(std::make_unique<asio::io_service::work>(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<char *>(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::thread>(
+ 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<memif_buffer_t *>(
+ malloc(sizeof(memif_buffer_t) * MAX_MEMIF_BUFS));
+ c->tx_buf_num = 0;
+ c->tx_bufs = static_cast<memif_buffer_t *>(
+ 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<const uint8_t *>((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<uint8_t *>(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 <hicn/transport/config.h>
+#include <hicn/transport/core/connector.h>
+#include <hicn/transport/core/hicn_binary_api.h>
+#include <hicn/transport/portability/portability.h>
+#include <hicn/transport/utils/epoll_event_reactor.h>
+#include <hicn/transport/utils/fd_deadline_timer.h>
+#include <hicn/transport/utils/ring_buffer.h>
+
+#include <asio.hpp>
+#include <deque>
+#include <mutex>
+#include <thread>
+
+#ifdef __vpp__
+
+#define _Static_assert static_assert
+
+extern "C" {
+#include <memif/libmemif.h>
+};
+
+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<std::thread> main_worker_;
+
+ int epfd;
+ std::unique_ptr<std::thread> memif_worker_;
+ utils::EpollEventReactor event_reactor_;
+ volatile bool timer_set_;
+ std::unique_ptr<utils::FdDeadlineTimer> send_timer_;
+ asio::io_service &io_service_;
+ std::unique_ptr<asio::io_service::work> 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 <hicn/transport/core/manifest_format.h>
+#include <hicn/transport/core/name.h>
+#include <hicn/transport/errors/errors.h>
+#include <hicn/transport/errors/tokenizer_exception.h>
+#include <hicn/transport/utils/hash.h>
+#include <hicn/transport/utils/string_tokenizer.h>
+
+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<std::uint32_t *>(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<Sockaddr> 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<Sockaddr>(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<transport::core::Name>::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 <hicn/transport/portability/portability.h>
+#include <hicn/transport/utils/branch_prediction.h>
+
+#include <list>
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+extern "C" {
+TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat")
+#include <hicn/hicn.h>
+};
+
+#include <vector>
+
+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<Sockaddr> 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<NameStruct> createEmptyName() {
+ NameStruct *name = new NameStruct;
+ name->type = HNT_UNSPEC;
+ return std::unique_ptr<NameStruct>(name);
+ };
+
+ std::unique_ptr<NameStruct> name_;
+};
+
+std::ostream &operator<<(std::ostream &os, const Name &name);
+
+} // end namespace core
+
+} // end namespace transport
+
+namespace std {
+template <>
+struct hash<transport::core::Name> {
+ 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 <hicn/transport/core/packet.h>
+#include <hicn/transport/errors/malformed_packet_exception.h>
+#include <hicn/transport/utils/hash.h>
+#include <hicn/transport/utils/log.h>
+
+extern "C" {
+TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat")
+#include <hicn/error.h>
+}
+
+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<utils::MemBuf> &&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<utils::MemBuf> &&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<uint8_t> Packet::getPayload() const {
+ if (TRANSPORT_EXPECT_FALSE(payload_head_ == nullptr)) {
+ return utils::Array<uint8_t>();
+ }
+
+ // Hopefully the payload is contiguous
+ if (TRANSPORT_EXPECT_FALSE(payload_head_->next() != header_head_)) {
+ payload_head_->gather(payloadSize());
+ }
+
+ return utils::Array<uint8_t>(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<utils::MemBuf> 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<utils::MemBuf> &&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 &timestamp) {
+ 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<utils::CryptoHashType>(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<Packet *>(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 <hicn/transport/core/name.h>
+#include <hicn/transport/core/payload_type.h>
+#include <hicn/transport/portability/portability.h>
+#include <hicn/transport/utils/branch_prediction.h>
+#include <hicn/transport/utils/crypto_hasher.h>
+#include <hicn/transport/utils/membuf.h>
+#include <hicn/transport/utils/object_pool.h>
+#include <hicn/transport/utils/crypto_suite.h>
+#include <hicn/transport/utils/key_id.h>
+
+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<Packet> {
+ friend class utils::Signer;
+ friend class utils::Verifier;
+
+ public:
+ using MemBufPtr = std::shared_ptr<utils::MemBuf>;
+ 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<utils::MemBuf> 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<utils::MemBuf> &&payload);
+
+ Packet &appendHeader(std::unique_ptr<utils::MemBuf> &&header);
+
+ Packet &appendHeader(const uint8_t *buffer, std::size_t length);
+
+ utils::Array<uint8_t> 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 &timestamp);
+
+ 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<utils::MemBuf> &&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 <hicn/transport/core/pending_interest.h>
+
+namespace transport {
+
+namespace core {
+
+PendingInterest::PendingInterest()
+ : interest_(nullptr, nullptr), timer_(), received_(false) {}
+
+PendingInterest::PendingInterest(Interest::Ptr &&interest,
+ std::unique_ptr<asio::steady_timer> &&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 <hicn/transport/core/content_object.h>
+#include <hicn/transport/core/interest.h>
+#include <hicn/transport/core/name.h>
+#include <hicn/transport/portability/portability.h>
+#include <hicn/transport/utils/deadline_timer.h>
+
+#include <asio/steady_timer.hpp>
+
+namespace transport {
+
+namespace core {
+
+class HicnForwarderInterface;
+class VPPForwarderInterface;
+class RawSocketInterface;
+
+template <typename ForwarderInt>
+class Portal;
+
+typedef std::function<void(const std::error_code &)> TimerCallback;
+
+class PendingInterest {
+ friend class Portal<HicnForwarderInterface>;
+ friend class Portal<VPPForwarderInterface>;
+ friend class Portal<RawSocketInterface>;
+
+ public:
+ PendingInterest();
+
+ PendingInterest(Interest::Ptr &&interest,
+ std::unique_ptr<asio::steady_timer> &&timer);
+
+ ~PendingInterest();
+
+ bool isReceived() const;
+
+ template <typename Handler>
+ 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<asio::steady_timer> 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 <hicn/transport/config.h>
+#include <hicn/transport/core/content_object.h>
+#include <hicn/transport/core/forwarder_interface.h>
+#include <hicn/transport/core/interest.h>
+#include <hicn/transport/core/name.h>
+#include <hicn/transport/core/pending_interest.h>
+#include <hicn/transport/core/prefix.h>
+#include <hicn/transport/core/socket_connector.h>
+#include <hicn/transport/errors/errors.h>
+#include <hicn/transport/portability/portability.h>
+#include <hicn/transport/utils/log.h>
+
+#ifdef __vpp__
+#include <hicn/transport/core/memif_connector.h>
+#endif
+
+#include <asio.hpp>
+#include <asio/steady_timer.hpp>
+#include <future>
+#include <memory>
+#include <unordered_map>
+
+#define UNSET_CALLBACK 0
+
+namespace transport {
+
+namespace core {
+
+typedef std::unordered_map<Name, std::unique_ptr<PendingInterest>>
+ PendingInterestHashTable;
+
+template <typename PrefixType>
+class BasicBindConfig {
+ static_assert(std::is_same<Prefix, PrefixType>::value,
+ "Prefix must be a Prefix type.");
+
+ const uint32_t standard_cs_reserved = 5000;
+
+ public:
+ template <typename T>
+ BasicBindConfig(T &&prefix)
+ : prefix_(std::forward<T &&>(prefix)),
+ content_store_reserved_(standard_cs_reserved) {}
+
+ template <typename T>
+ BasicBindConfig(T &&prefix, uint32_t cs_reserved)
+ : prefix_(std::forward<T &&>(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<Prefix>;
+
+template <typename ForwarderInt>
+class Portal {
+ static_assert(
+ std::is_base_of<ForwarderInterface<ForwarderInt,
+ typename ForwarderInt::ConnectorType>,
+ 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<asio::io_service::work>(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<PendingInterest>(
+ std::move(interest), std::make_unique<asio::steady_timer>(io_service_));
+
+ pending_interest_hash_table_[name]->startCountdown(
+ std::bind(&Portal<ForwarderInt>::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<Name, std::unique_ptr<PendingInterest>>::iterator it =
+ pending_interest_hash_table_.find(name);
+ if (it != pending_interest_hash_table_.end()) {
+ std::unique_ptr<PendingInterest> 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<PendingInterest> 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<asio::io_service::work> 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<Prefix> 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 <hicn/transport/core/prefix.h>
+#include <hicn/transport/errors/errors.h>
+#include <hicn/transport/utils/string_tokenizer.h>
+
+extern "C" {
+#include <arpa/inet.h>
+}
+
+#include <cstring>
+#include <memory>
+#include <random>
+
+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<Sockaddr> 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<Sockaddr>(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<uint32_t> idis(
+ 0, std::numeric_limits<uint32_t>::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 <hicn/transport/core/name.h>
+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<Sockaddr> 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 <hicn/transport/core/raw_socket_connector.h>
+#include <hicn/transport/utils/conversions.h>
+#include <hicn/transport/utils/log.h>
+
+#include <net/if.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#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(&ethernet_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<int>(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 &>(packet);
+ // p.setTcpChecksum();
+
+ // if (!p.checkIntegrity()) {
+ // TRANSPORT_LOGW("Sending message with wrong checksum!!!");
+ // }
+
+ // std::shared_ptr<const Packet> 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>(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<asio::const_buffer>();
+
+ 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<uint8_t *>(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 <hicn/transport/core/connector.h>
+#include <hicn/transport/core/name.h>
+
+#include <linux/if_packet.h>
+#include <net/ethernet.h>
+#include <sys/socket.h>
+#include <asio.hpp>
+#include <deque>
+
+namespace transport {
+
+namespace core {
+
+using asio::generic::raw_protocol;
+using raw_endpoint = asio::generic::basic_endpoint<raw_protocol>;
+
+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<utils::MemBuf>::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 <hicn/transport/core/raw_socket_interface.h>
+#include <hicn/transport/utils/linux.h>
+
+#include <fstream>
+
+namespace transport {
+
+namespace core {
+
+static std::string config_folder_path = "/etc/transport/interface.conf.d";
+
+RawSocketInterface::RawSocketInterface(RawSocketConnector &connector)
+ : ForwarderInterface<RawSocketInterface, RawSocketConnector>(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 <hicn/transport/core/forwarder_interface.h>
+#include <hicn/transport/core/prefix.h>
+#include <hicn/transport/core/raw_socket_connector.h>
+
+#include <atomic>
+#include <deque>
+
+namespace transport {
+
+namespace core {
+
+class RawSocketInterface
+ : public ForwarderInterface<RawSocketInterface, RawSocketConnector> {
+ 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 <hicn/transport/core/socket_connector.h>
+#include <hicn/transport/errors/errors.h>
+#include <hicn/transport/utils/log.h>
+#include <hicn/transport/utils/object_pool.h>
+
+#include <vector>
+
+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<asio::const_buffer>();
+
+ 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 <hicn/transport/core/connector.h>
+#include <hicn/transport/core/name.h>
+#include <hicn/transport/utils/branch_prediction.h>
+
+#include <asio.hpp>
+#include <deque>
+
+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<utils::MemBuf>::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 <gtest/gtest.h>
+
+#include "../manifest_format_fixed.h"
+#include "../manifest_inline.h"
+
+#include <test.h>
+#include <random>
+#include <vector>
+
+namespace transport {
+
+namespace core {
+
+namespace {
+// The fixture for testing class Foo.
+class ManifestTest : public ::testing::Test {
+ protected:
+ using ContentObjectManifest = ManifestInline<ContentObject, Fixed>;
+
+ 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<uint8_t> 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<std::default_random_engine, CHAR_BIT,
+ unsigned char>;
+ random_bytes_engine rbe;
+
+ std::default_random_engine eng((std::random_device())());
+ std::uniform_int_distribution<uint64_t> idis(
+ 0, std::numeric_limits<uint32_t>::max());
+
+ auto entries = new std::pair<uint32_t, utils::CryptoHash>[3];
+ uint32_t suffixes[3];
+ std::vector<unsigned char> 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<uint8_t>().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<uint64_t> idis(
+ 0, std::numeric_limits<uint64_t>::max());
+
+ using random_bytes_engine =
+ std::independent_bits_engine<std::default_random_engine, CHAR_BIT,
+ unsigned char>;
+ random_bytes_engine rbe;
+
+ while (manifest1_.estimateManifestSize(1) < 1440) {
+ uint32_t suffix = static_cast<std::uint32_t>(idis(eng));
+ std::vector<unsigned char> 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 <hicn/transport/config.h>
+
+#ifdef __vpp__
+
+#include <hicn/transport/core/vpp_binary_api.h>
+#include <hicn/transport/core/vpp_binary_api_internal.h>
+#include <hicn/transport/utils/log.h>
+
+#include <vat/vat.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+#include <vnet/ip/ip.h>
+#include <vppinfra/error.h>
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <vpp/api/vpe_msg_enum.h>
+
+#define vl_typedefs
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_typedefs
+
+#define vl_endianfun
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_endianfun
+
+#define vl_print(handle, ...)
+#define vl_printfun
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_printfun
+
+/* Get CRC codes of the messages */
+#define vl_msg_name_crc_list
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_msg_name_crc_list
+
+// #define vl_api_version(n,v) static u32 vpe_api_version = (v);
+// #include <vpp/api/vpe.api.h>
+// #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 <hicn/transport/config.h>
+
+#ifdef __vpp__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdlib.h>
+
+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 <hicn/transport/config.h>
+
+#ifdef __vpp__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <semaphore.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+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 <hicn/transport/config.h>
+
+#ifdef __vpp__
+
+#include <hicn/transport/core/hicn_binary_api.h>
+#include <hicn/transport/core/memif_binary_api.h>
+#include <hicn/transport/core/vpp_forwarder_interface.h>
+
+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<VPPForwarderInterface, MemifConnector>(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<std::mutex> 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_, &params) < 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 <hicn/transport/config.h>
+
+#ifdef __vpp__
+
+#include <hicn/transport/core/forwarder_interface.h>
+#include <hicn/transport/core/memif_connector.h>
+#include <hicn/transport/core/prefix.h>
+
+#include <deque>
+
+namespace transport {
+
+namespace core {
+
+class VPPForwarderInterface
+ : public ForwarderInterface<VPPForwarderInterface, MemifConnector> {
+ 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 <hicn/transport/errors/invalid_ip_address_exception.h>
+#include <hicn/transport/errors/malformed_name_exception.h>
+#include <hicn/transport/errors/malformed_packet_exception.h>
+#include <hicn/transport/errors/not_implemented_exception.h>
+#include <hicn/transport/errors/null_pointer_exception.h>
+#include <hicn/transport/errors/runtime_exception.h>
+#include <hicn/transport/errors/tokenizer_exception.h> \ 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 <stdexcept>
+
+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 <stdexcept>
+
+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 <stdexcept>
+
+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 <stdexcept>
+
+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 <stdexcept>
+
+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 <stdexcept>
+
+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 <iostream>
+#include <sstream>
+#include <stdexcept>
+#include <string>
+
+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 <stdexcept>
+
+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 <hicn/transport/http/server_publisher.h>
+
+#include <functional>
+#include <memory>
+
+namespace transport {
+
+namespace http {
+
+enum class RC : uint8_t {
+ SUCCESS,
+ CONTENT_PUBLISHED,
+ ERR_UNDEFINED,
+};
+
+using OnHttpRequest =
+ std::function<void(std::shared_ptr<HTTPServerPublisher>&, const uint8_t*,
+ std::size_t, int request_id)>;
+using DeadlineTimerCallback = std::function<void(const std::error_code& e)>;
+using ReceiveCallback = std::function<void(const std::vector<uint8_t>&)>;
+using OnPayloadCallback = std::function<RC(
+ const std::error_code& ec, const core::Name& name,
+ const std::shared_ptr<utils::SharableVector<uint8_t>>& payload)>;
+using ContentSentCallback =
+ std::function<void(const std::error_code&, const core::Name&)>;
+
+} // 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 <hicn/transport/http/client_connection.h>
+#include <hicn/transport/utils/hash.h>
+
+#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<HTTPResponse>()),
+ 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<typename ConsumerSocket::Portal> portal;
+ consumer_.getSocketOption(GeneralTransportOptions::PORTAL, portal);
+ timer_ = std::make_unique<asio::steady_timer>(portal->getIoService());
+}
+
+HTTPClientConnection &HTTPClientConnection::get(
+ const std::string &url, HTTPHeaders headers, HTTPPayload payload,
+ std::shared_ptr<HTTPResponse> 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<HTTPResponse> 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<std::chrono::microseconds>(
+ end - start)
+ .count(),
+ response->size());
+
+ return *this;
+}
+
+std::string HTTPClientConnection::sendRequestGetReply(
+ const HTTPRequest &request, std::shared_ptr<HTTPResponse> &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 &>(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 <hicn/transport/http/default_values.h>
+#include <hicn/transport/http/request.h>
+#include <hicn/transport/http/response.h>
+#include <hicn/transport/interfaces/socket_consumer.h>
+#include <hicn/transport/interfaces/socket_producer.h>
+#include <hicn/transport/utils/uri.h>
+
+#include <vector>
+
+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<HTTPResponse> response = nullptr);
+
+ HTTPClientConnection &sendRequest(
+ const std::string &url, HTTPMethod method, HTTPHeaders headers = {},
+ HTTPPayload payload = {},
+ std::shared_ptr<HTTPResponse> 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<HTTPResponse> &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<HTTPResponse> response_;
+
+ std::unique_ptr<asio::steady_timer> 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 <cstdint>
+
+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 <hicn/transport/http/transport_http_client_connection.h>
+#include <hicn/transport/http/transport_http_server_acceptor.h>
+#include <hicn/transport/http/transport_http_server_publisher.h>
+
+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 <hicn/transport/utils/sharable_vector.h>
+
+#include <map>
+#include <sstream>
+#include <vector>
+
+#define HTTP_VERSION "1.1"
+
+namespace transport {
+
+namespace http {
+
+typedef enum { GET, POST, PUT, PATCH, DELETE } HTTPMethod;
+
+static std::map<HTTPMethod, std::string> method_map = {
+ {GET, "GET"}, {POST, "POST"}, {PUT, "PUT"},
+ {PATCH, "PATCH"}, {DELETE, "DELETE"},
+};
+
+typedef std::map<std::string, std::string> HTTPHeaders;
+typedef std::vector<uint8_t> 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 <hicn/transport/http/request.h>
+#include <hicn/transport/utils/uri.h>
+
+namespace transport {
+
+namespace http {
+
+// std::map<HTTPMethod, std::string> 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 <hicn/transport/http/message.h>
+#include <hicn/transport/utils/sharable_vector.h>
+
+#include <map>
+#include <sstream>
+#include <vector>
+
+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 <hicn/transport/errors/errors.h>
+#include <hicn/transport/http/response.h>
+
+#include <algorithm>
+
+#include <cstring>
+
+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<std::vector<uint8_t> *>(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 <hicn/transport/http/message.h>
+#include <hicn/transport/utils/array.h>
+#include <hicn/transport/utils/sharable_vector.h>
+
+#include <map>
+#include <sstream>
+#include <vector>
+
+namespace transport {
+
+namespace http {
+
+class HTTPResponse : public HTTPMessage, public utils::SharableVector<uint8_t> {
+ 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 <hicn/transport/http/server_acceptor.h>
+#include <hicn/transport/utils/hash.h>
+#include <hicn/transport/utils/uri.h>
+
+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<ProducerSocket>(
+ 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<uint8_t> 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<HTTPServerPublisher>(interest.getName());
+ callback_(publishers_[request_id], (uint8_t *)payload.data(),
+ payload.length(), request_id);
+}
+
+std::map<int, std::shared_ptr<HTTPServerPublisher>>
+ &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 <hicn/transport/http/callbacks.h>
+#include <hicn/transport/http/default_values.h>
+#include <hicn/transport/http/request.h>
+#include <hicn/transport/http/server_publisher.h>
+#include <hicn/transport/interfaces/socket_consumer.h>
+#include <hicn/transport/interfaces/socket_producer.h>
+
+#include <functional>
+#include <vector>
+
+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<int, std::shared_ptr<HTTPServerPublisher>> &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<ProducerSocket> acceptor_producer_;
+
+ std::map<int, std::shared_ptr<HTTPServerPublisher>> 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 <hicn/transport/http/server_publisher.h>
+#include <hicn/transport/utils/literals.h>
+
+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<ProducerSocket>(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<typename ProducerSocket::Portal> portal;
+ producer_->getSocketOption(GeneralTransportOptions::PORTAL, portal);
+ timer_ =
+ std::make_unique<asio::steady_timer>(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<uint32_t>(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 <typename Handler>
+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<uint32_t>(content_lifetime.count()));
+ producer_->asyncProduce(content_name_, buf, buffer_size,
+ std::forward<Handler>(handler), is_last);
+ }
+}
+
+void HTTPServerPublisher::serveClients() { producer_->serveForever(); }
+
+void HTTPServerPublisher::stop() {
+ std::shared_ptr<typename ProducerSocket::Portal> 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<core::Sockaddr> 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<ContentObjectManifest>(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 <hicn/transport/http/default_values.h>
+#include <hicn/transport/interfaces/socket_consumer.h>
+#include <hicn/transport/interfaces/socket_producer.h>
+
+#include <functional>
+#include <vector>
+
+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 <typename Handler>
+ 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<asio::steady_timer> timer_;
+ asio::io_service io_service_;
+ std::unique_ptr<ProducerSocket> producer_;
+ ProducerInterestCallback interest_enter_callback_;
+ utils::UserCallback wait_callback_;
+
+ utils::SharableVector<uint8_t> 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 <hicn/transport/interfaces/publication_options.h>
+#include <hicn/transport/portability/portability.h>
+#include <hicn/transport/utils/sharable_vector.h>
+
+#include <sys/uio.h>
+#include <memory>
+
+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<WriteFlags>(static_cast<uint32_t>(a) |
+ static_cast<uint32_t>(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<WriteFlags>(static_cast<uint32_t>(a) &
+ static_cast<uint32_t>(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<WriteFlags>(~static_cast<uint32_t>(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<uint8_t> &&buffer) noexcept {}
+
+ // virtual void readBufferAvailable(utils::SharableBuffer<uint8_t>&& 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<uint8_t> &&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<IOBuf>&& 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 <hicn/transport/interfaces/full_duplex_socket.h>
+#include <hicn/transport/interfaces/socket_options_default_values.h>
+#include <hicn/transport/utils/sharable_vector.h>
+
+#include <memory>
+
+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<ProducerSocket>(io_service_)),
+ consumer_(std::make_unique<ConsumerSocket>(
+ 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<utils::SharableVector<uint8_t>>()) {
+ 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<ActionMessage *>(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<uint8_t *>(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<const uint8_t *>(buf), bytes);
+ signalProductionToSubscribers(options.name);
+ } else {
+ TRANSPORT_LOGI("Sending payload through interest");
+ piggybackPayloadToSubscribers(
+ options.name, reinterpret_cast<const uint8_t *>(buf), bytes);
+ }
+}
+
+void AsyncFullDuplexSocket::write(
+ WriteCallback *callback, utils::SharableVector<uint8_t> &&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<PayloadMessage *>(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<uint8_t *>(interest->getPayload().data());
+
+ ActionMessage *produce_notification =
+ reinterpret_cast<ActionMessage *>(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<uint8_t *>(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<core::ContentObject>
+AsyncFullDuplexSocket::decodeSynchronizationMessage(
+ const core::Interest &interest) {
+ auto mesg = interest.getPayload();
+ const MessageHeader *header =
+ reinterpret_cast<const MessageHeader *>(mesg.data());
+
+ switch (header->msg_type) {
+ case MessageType::ACTION: {
+ // Check what is the action to perform
+ const ActionMessage *message =
+ reinterpret_cast<const ActionMessage *>(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<utils::SharableVector<uint8_t>>();
+ 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<core::ContentObject>(nullptr);
+ }
+ }
+
+ return std::shared_ptr<core::ContentObject>(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<SubscriptionResponseMessage *>(
+ 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<core::ContentObject> AsyncFullDuplexSocket::createAck() {
+ // Send the response back
+ core::Name name("b001::abcd");
+ auto response = std::make_shared<core::ContentObject>(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<core::ContentObject>
+AsyncFullDuplexSocket::createSubscriptionResponse(const core::Name &name) {
+ // Send the response back
+ core::Name tmp_name("b001::abcd");
+ auto response = std::make_shared<core::ContentObject>(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<uint8_t *>(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 <hicn/transport/core/prefix.h>
+#include <hicn/transport/interfaces/async_transport.h>
+#include <hicn/transport/interfaces/socket_consumer.h>
+#include <hicn/transport/interfaces/socket_producer.h>
+#include <hicn/transport/portability/portability.h>
+#include <hicn/transport/utils/sharable_vector.h>
+
+#include <unordered_set>
+#include <vector>
+
+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<AsyncFullDuplexSocket>;
+ using SharedPtr = std::unique_ptr<AsyncFullDuplexSocket>;
+
+ 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<uint8_t> &&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<core::ContentObject> 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<core::ContentObject> createAck();
+ std::shared_ptr<core::ContentObject> createSubscriptionResponse(
+ const core::Name &name);
+
+ core::Prefix locator_;
+ uint32_t incremental_suffix_;
+ core::Name sync_notification_;
+ // std::unique_ptr<BasePortal> 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<core::Name> subscribers_;
+
+ // Useful for publishing / Retrieving data
+ std::unique_ptr<ProducerSocket> producer_;
+ std::unique_ptr<ConsumerSocket> consumer_;
+
+ ReadCallback *read_callback_;
+ WriteCallback *write_callback_;
+ ConnectCallback *connect_callback_;
+ AcceptCallback *accept_callback_;
+
+ std::unique_ptr<OnConnectCallback> internal_connect_callback_;
+ std::unique_ptr<OnSignalCallback> internal_signal_callback_;
+
+ uint32_t send_timeout_milliseconds_;
+ struct Counters counters_;
+ std::shared_ptr<utils::SharableVector<uint8_t>> 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 <map>
+#include <sstream>
+#include <vector>
+
+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 <hicn/transport/interfaces/rtc_socket_consumer.h>
+
+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<RTCTransportProtocol *>(
+ 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 <hicn/transport/interfaces/socket_consumer.h>
+
+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 <stdlib.h>
+#include <time.h>
+#include <hicn/transport/interfaces/rtc_socket_producer.h>
+
+#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<ContentObject>()),
+ 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<std::chrono::milliseconds>(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<ContentObject> content_object =
+ std::make_shared<ContentObject>(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::milliseconds>(
+ 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 <hicn/transport/interfaces/socket_producer.h>
+#include <hicn/transport/utils/content_store.h>
+
+#include <map>
+#include <mutex>
+
+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<uint32_t, uint64_t> pendingInterests_;
+ uint32_t currentSeg_;
+ uint32_t prodLabel_;
+ uint16_t headerSize_;
+ Name flowName_;
+ // bool produceInSynch_;
+ std::shared_ptr<ContentObject> 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 <hicn/transport/config.h>
+#include <hicn/transport/core/content_object.h>
+#include <hicn/transport/core/facade.h>
+#include <hicn/transport/core/interest.h>
+#include <hicn/transport/core/manifest_format_fixed.h>
+#include <hicn/transport/core/manifest_inline.h>
+#include <hicn/transport/core/name.h>
+#include <hicn/transport/interfaces/socket_options_default_values.h>
+#include <hicn/transport/interfaces/socket_options_keys.h>
+#include <hicn/transport/utils/crypto_suite.h>
+#include <hicn/transport/utils/identity.h>
+#include <hicn/transport/utils/verifier.h>
+
+#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 <typename PortalType>
+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<VPPForwarderPortal>;
+using BasePortal = VPPForwarderPortal;
+#else
+using BaseSocket = Socket<HicnForwarderPortal>;
+using BasePortal = HicnForwarderPortal;
+#endif
+
+using PayloadType = core::PayloadType;
+using Prefix = core::Prefix;
+using Array = utils::Array<uint8_t>;
+
+using ConsumerInterestCallback =
+ std::function<void(ConsumerSocket &, const core::Interest &)>;
+
+using ConsumerContentCallback =
+ std::function<void(ConsumerSocket &, std::size_t, const std::error_code &)>;
+
+using ConsumerTimerCallback =
+ std::function<void(ConsumerSocket &, std::size_t,
+ std::chrono::milliseconds &, float, uint32_t, uint32_t)>;
+
+using ProducerContentCallback = std::function<void(
+ ProducerSocket &, const std::error_code &, uint64_t bytes_written)>;
+
+using ConsumerContentObjectCallback =
+ std::function<void(ConsumerSocket &, const core::ContentObject &)>;
+
+using ConsumerContentObjectVerificationCallback =
+ std::function<bool(ConsumerSocket &, const core::ContentObject &)>;
+
+using ConsumerManifestCallback =
+ std::function<void(ConsumerSocket &, const core::ContentObjectManifest &)>;
+
+using ProducerContentObjectCallback =
+ std::function<void(ProducerSocket &, core::ContentObject &)>;
+
+using ProducerInterestCallback =
+ std::function<void(ProducerSocket &, const core::Interest &)>;
+
+using ProducerInterestCallback =
+ std::function<void(ProducerSocket &, const core::Interest &)>;
+
+using namespace protocol;
+
+template <typename PortalType>
+class Socket {
+ static_assert(std::is_same<PortalType, HicnForwarderPortal>::value
+#ifdef __linux__
+#ifndef __ANDROID__
+ || std::is_same<PortalType, RawSocketPortal>::value
+#ifdef __vpp__
+ || std::is_same<PortalType, VPPForwarderPortal>::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<Prefix> 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<Prefix> &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<Portal> &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 <hicn/transport/interfaces/socket_consumer.h>
+
+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<Portal>(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<VegasTransportProtocol>(this);
+ break;
+ case TransportProtocolAlgorithms::CBR:
+ transport_protocol_ = std::make_shared<CbrTransportProtocol>(this);
+ break;
+ case TransportProtocolAlgorithms::RTC:
+ transport_protocol_ = std::make_shared<RTCTransportProtocol>(this);
+ break;
+ case TransportProtocolAlgorithms::RAAQM:
+ default:
+ transport_protocol_ = std::make_shared<RaaqmTransportProtocol>(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<uint8_t> &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<utils::SharableVector<uint8_t>> 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<Prefix> 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<Prefix> &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<Portal> &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 <hicn/transport/interfaces/socket.h>
+
+#include <hicn/transport/protocols/cbr.h>
+#include <hicn/transport/protocols/protocol.h>
+#include <hicn/transport/protocols/raaqm.h>
+#include <hicn/transport/protocols/rtc.h>
+#include <hicn/transport/protocols/vegas.h>
+
+#include <hicn/transport/utils/event_thread.h>
+#include <hicn/transport/utils/sharable_vector.h>
+
+#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<uint8_t> &receive_buffer);
+
+ int asyncConsume(
+ const Name &name,
+ std::shared_ptr<utils::SharableVector<uint8_t>> 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<Prefix> 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<Prefix> &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<Portal> &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<TransportProtocol> transport_protocol_;
+
+ private:
+ // context inner state variables
+ asio::io_service internal_io_service_;
+ asio::io_service &io_service_;
+
+ std::shared_ptr<Portal> 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<utils::SharableVector<uint8_t>> 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 <chrono>
+#include <cstdint>
+
+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 <hicn/transport/interfaces/socket_producer.h>
+
+namespace transport {
+
+namespace interface {
+
+typedef std::chrono::time_point<std::chrono::steady_clock> 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<Portal>(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<ContentObject> &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<Name, std::shared_ptr<const Interest>>::iterator it;
+
+ {
+ std::lock_guard<std::mutex> 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<std::mutex> 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<ContentObject>(
+ 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<Name, std::shared_ptr<const Interest>>::iterator it;
+
+ {
+ std::lock_guard<std::mutex> 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<std::mutex> 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<ContentObjectManifest> manifest;
+ bool is_last_manifest = false;
+ std::unique_ptr<utils::CryptoHash> 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<float>(
+ std::floor(double(data_packet_size_ - manifest_header_size -
+ ContentObjectManifest::getManifestHeaderSize()) /
+ (4.0 + 32.0)) -
+ 1.0);
+ auto number_of_manifests = static_cast<uint32_t>(
+ 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<uint32_t>::max());
+ }
+
+ uint8_t hash[hash_size];
+ std::memset(hash, 0, hash_size);
+ zero_hash = std::make_unique<utils::CryptoHash>(
+ hash, hash_size, static_cast<utils::CryptoHashType>(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<uint32_t>::max());
+ }
+ current_segment++;
+ }
+ }
+
+ auto content_object = std::make_shared<ContentObject>(
+ 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<uint8_t> &&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<ContentObject> 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<std::mutex> lock(pending_interests_mtx_);
+ pending_interests_[interest.getName()] =
+ std::static_pointer_cast<const Interest>(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<Prefix> 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<utils::Identity>(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<Prefix> &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<Portal> &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 <hicn/transport/interfaces/socket.h>
+#include <hicn/transport/utils/content_store.h>
+#include <hicn/transport/utils/event_thread.h>
+#include <hicn/transport/utils/sharable_vector.h>
+
+#include <atomic>
+#include <cmath>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+#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<BasePortal>,
+ 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<uint8_t> &&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<Prefix> 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<Prefix> &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<Portal> &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> portal_;
+ std::size_t data_packet_size_;
+ std::list<Prefix> 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<Name, std::unordered_map<int, uint32_t>> seq_number_map_;
+
+ int signature_type_;
+ int signature_size_;
+
+ HashAlgorithm hash_algorithm_;
+ utils::CryptoSuite crypto_suite_;
+ std::unique_ptr<utils::Identity> identity_;
+ // utils::Signer& signer_;
+
+ // buffers
+
+ std::queue<std::shared_ptr<const Interest>> 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<Name, std::shared_ptr<const Interest>> 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<ContentObject> &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 <hicn/transport/portability/c_portability.h>
+
+#include <string.h>
+#include <cstddef>
+
+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 <hicn/transport/interfaces/socket_consumer.h>
+#include <hicn/transport/protocols/cbr.h>
+
+namespace transport {
+
+namespace protocol {
+
+using namespace interface;
+
+CbrTransportProtocol::CbrTransportProtocol(BaseSocket *icnet_socket)
+ : VegasTransportProtocol(icnet_socket) {}
+
+void CbrTransportProtocol::start(
+ utils::SharableVector<uint8_t> &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 <hicn/transport/protocols/raaqm_data_path.h>
+#include <hicn/transport/protocols/rate_estimation.h>
+#include <hicn/transport/protocols/vegas.h>
+#include <hicn/transport/protocols/vegas_rto_estimator.h>
+
+namespace transport {
+
+namespace protocol {
+
+class CbrTransportProtocol : public VegasTransportProtocol {
+ public:
+ CbrTransportProtocol(interface::BaseSocket *icnet_socket);
+
+ void start(utils::SharableVector<uint8_t> &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 <hicn/transport/interfaces/socket_consumer.h>
+#include <hicn/transport/protocols/protocol.h>
+
+namespace transport {
+
+namespace protocol {
+
+TransportProtocol::TransportProtocol(interface::BaseSocket *icn_socket)
+ : socket_(dynamic_cast<interface::ConsumerSocket *>(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 <hicn/transport/interfaces/socket.h>
+#include <hicn/transport/utils/object_pool.h>
+#include <hicn/transport/utils/sharable_vector.h>
+
+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<uint8_t> &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<interface::BasePortal> portal_;
+ volatile bool is_running_;
+ utils::ObjectPool<Interest> 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 <hicn/transport/interfaces/socket_consumer.h>
+#include <hicn/transport/protocols/raaqm.h>
+
+#include <fstream>
+
+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<uint8_t> &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<RaaqmDataPath>(
+ 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<RaaqmDataPath> new_path =
+ std::make_shared<RaaqmDataPath>(
+ *(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<std::chrono::microseconds>(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<uint32_t, std::shared_ptr<RaaqmDataPath>>::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<uint32_t, std::shared_ptr<RaaqmDataPath>>::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 <hicn/transport/protocols/raaqm_data_path.h>
+#include <hicn/transport/protocols/rate_estimation.h>
+#include <hicn/transport/protocols/vegas.h>
+#include <hicn/transport/protocols/vegas_rto_estimator.h>
+
+namespace transport {
+
+namespace protocol {
+
+class RaaqmTransportProtocol : public VegasTransportProtocol {
+ public:
+ RaaqmTransportProtocol(interface::BaseSocket *icnet_socket);
+
+ ~RaaqmTransportProtocol();
+
+ void start(utils::SharableVector<uint8_t> &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<RaaqmDataPath> cur_path_;
+
+ /**
+ * Hash table for path: each entry is a pair path ID(key) - path object
+ */
+ std::unordered_map<uint32_t, std::shared_ptr<RaaqmDataPath>> 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 <hicn/transport/protocols/raaqm_data_path.h>
+
+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<unsigned>(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 <hicn/transport/utils/min_filter.h>
+
+#include <sys/time.h>
+#include <climits>
+#include <iostream>
+
+#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<uint64_t> 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 <hicn/transport/protocols/rate_estimation.h>
+#include <hicn/transport/utils/log.h>
+
+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 <unistd.h>
+
+#include <hicn/transport/protocols/download_observer.h>
+#include <hicn/transport/protocols/raaqm_data_path.h>
+
+#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 <math.h>
+
+#include <hicn/transport/interfaces/socket_consumer.h>
+#include <hicn/transport/protocols/rtc.h>
+
+/*
+ * 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<uint8_t> &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::milliseconds>(
+ 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<RTCDataPath> newPath = std::make_shared<RTCDataPath>();
+ pathTable_[pathLabel] = newPath;
+ }
+
+ // RTT measurements are useful both from NACKs and data packets
+ uint64_t RTT = std::chrono::duration_cast<std::chrono::milliseconds>(
+ 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::milliseconds>(
+ 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<uint32_t, uint64_t>::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::milliseconds>(
+ 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<ConsumerSocket *>(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<uint32_t, uint64_t>::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::milliseconds>(
+ 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<ConsumerSocket *>(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<uint32_t> &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<uint32_t, uint64_t>::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<uint32_t, uint64_t>::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<ConsumerSocket *>(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<uint32_t> 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 <queue>
+#include <set>
+#include <unordered_map>
+
+#include <hicn/transport/protocols/protocol.h>
+#include <hicn/transport/protocols/rtc_data_path.h>
+
+// 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<uint8_t> &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<uint32_t> &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<uint32_t> interestRetransmissions_;
+ std::vector<sentInterest> inflightInterests_;
+ uint32_t nackedByProducerMaxSize_;
+ std::set<uint32_t>
+ 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<utils::SharableVector<uint8_t>> 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<uint32_t, std::shared_ptr<RTCDataPath>> pathTable_;
+ uint32_t roundCounter_;
+ // std::vector<uint64_t> minRTTwin_;
+ uint64_t minRtt_;
+
+ std::unordered_map<uint32_t, uint64_t> 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 <hicn/transport/protocols/rtc_data_path.h>
+
+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 <stdint.h>
+#include <hicn/transport/utils/min_filter.h>
+#include <climits>
+
+#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<uint64_t> RTThistory_;
+ utils::MinFilter<int64_t> 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 <gtest/gtest.h>
+
+#include "../socket_producer.h"
+#include "literals.h"
+
+#include <test.h>
+#include <random>
+
+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<const uint8_t *>(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 <hicn/transport/errors/not_implemented_exception.h>
+#include <hicn/transport/interfaces/socket_consumer.h>
+#include <hicn/transport/protocols/vegas.h>
+#include <hicn/transport/utils/literals.h>
+
+#include <cmath>
+
+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<uint32_t>::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<uint32_t>::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<uint8_t> &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<std::chrono::microseconds>(duration));
+
+ RtoEstimator::Duration rto = rtt_estimator_.computeRto();
+ std::chrono::milliseconds lifetime =
+ std::chrono::duration_cast<std::chrono::milliseconds>(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<ContentObjectManifest> &&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<uint8_t>(_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<bool>(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<std::chrono::microseconds>(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<TimeDuration>(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<ContentObjectManifest>(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<ConsumerSocket *>(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<utils::CryptoHashType>(it->second.second);
+ auto data_packet_digest = content_object.computeDigest(it->second.second);
+ auto data_packet_digest_bytes =
+ data_packet_digest.getDigest<uint8_t>().data();
+ std::vector<uint8_t> &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<bool>(
+ dynamic_cast<ConsumerSocket *>(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<Interest> retx_interest =
+// std::make_shared<Interest>(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 <hicn/transport/protocols/protocol.h>
+#include <hicn/transport/protocols/vegas_rto_estimator.h>
+#include <hicn/transport/utils/event_thread.h>
+#include <hicn/transport/utils/ring_buffer.h>
+#include <hicn/transport/utils/sharable_vector.h>
+
+#include <map>
+
+namespace transport {
+
+namespace protocol {
+
+typedef utils::CircularFifo<uint32_t, 1024 * 128> SuffixQueue;
+typedef std::chrono::time_point<std::chrono::steady_clock> Time;
+typedef std::chrono::milliseconds TimeDuration;
+
+class VegasTransportProtocol : public TransportProtocol {
+ public:
+ VegasTransportProtocol(interface::BaseSocket *icnet_socket);
+
+ virtual ~VegasTransportProtocol();
+
+ virtual void start(utils::SharableVector<uint8_t> &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<interface::ContentObjectManifest> &&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<uint64_t> final_block_number_;
+ uint64_t last_reassembled_segment_;
+ std::shared_ptr<utils::SharableVector<uint8_t>> 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<std::uint32_t> interest_retransmissions_;
+ std::vector<std::chrono::steady_clock::time_point> interest_timepoints_;
+ RtoEstimator rtt_estimator_;
+
+ uint32_t retx_count_;
+
+ // buffers
+ std::unordered_map<std::uint32_t, ContentObject::Ptr>
+ receive_buffer_; // verified segments by segment number
+ std::unordered_map<std::uint32_t, ContentObject::Ptr>
+ unverified_segments_; // used with embedded manifests
+ std::unordered_map<std::uint32_t, ContentObject::Ptr>
+ 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<std::uint64_t, std::uint64_t>
+ incremental_suffix_to_real_suffix_map_;
+ std::unordered_map<std::uint64_t, std::uint64_t>
+ real_suffix_to_incremental_suffix_map_;
+ std::uint32_t incremental_suffix_index_;
+
+ // verification
+ std::unordered_map<uint32_t, std::pair<std::vector<uint8_t>, HashAlgorithm>>
+ suffix_hash_map_;
+
+ // Fast Retransmission
+ std::map<uint64_t, bool> received_segments_;
+ std::unordered_map<uint64_t, bool> fast_retransmitted_segments;
+
+ // Suffix queue
+ volatile bool suffix_queue_completed_;
+ SuffixQueue suffix_queue_;
+
+ volatile bool download_with_manifest_;
+ uint32_t next_manifest_;
+ std::atomic<uint16_t> next_manifest_interval_;
+
+ std::unique_ptr<utils::EventThread> verifier_thread_;
+
+ uint32_t interest_tx_;
+ uint32_t interest_count_;
+
+ uint64_t byte_count_;
+ double average_rtt_;
+
+ std::unordered_map<uint32_t, uint64_t> 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 <hicn/transport/interfaces/socket_options_default_values.h>
+#include <hicn/transport/protocols/vegas_rto_estimator.h>
+
+#include <algorithm>
+#include <cmath>
+
+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<double>(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<Duration::rep>(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 <chrono>
+
+// 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 <hicn/transport/portability/portability.h>
+
+#include <cstddef>
+
+namespace utils {
+
+template <typename T>
+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<T *>(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 <hicn/transport/core/content_object.h>
+#include <hicn/transport/core/interest.h>
+#include <hicn/transport/core/name.h>
+#include <hicn/transport/utils/content_store.h>
+
+namespace utils {
+
+ContentStore::ContentStore(std::size_t max_packets)
+ : max_content_store_size_(max_packets) {}
+
+ContentStore::~ContentStore() {}
+
+void ContentStore::insert(
+ const std::shared_ptr<ContentObject> &content_object) {
+ if (max_content_store_size_ == 0) {
+ return;
+ }
+
+ std::unique_lock<std::mutex> 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<ContentObject> &ContentStore::find(
+ const Interest &interest) {
+ std::unique_lock<std::mutex> 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::milliseconds>(
+ // 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<std::mutex> 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 <hicn/transport/interfaces/socket.h>
+
+#include <mutex>
+
+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::shared_ptr<ContentObject>,
+ std::chrono::steady_clock::time_point>
+ ObjectTimeEntry;
+typedef std::pair<ObjectTimeEntry,
+ std::list<std::reference_wrapper<const Name>>::iterator>
+ ContentStoreEntry;
+typedef std::list<std::reference_wrapper<const Name>> LRUList;
+typedef std::unordered_map<Name, ContentStoreEntry> ContentStoreHashTable;
+
+class ContentStore {
+ public:
+ explicit ContentStore(std::size_t max_packets = 65536);
+
+ ~ContentStore();
+
+ void insert(const std::shared_ptr<ContentObject> &content_object);
+
+ const std::shared_ptr<ContentObject> &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<ContentObject> 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 <hicn/transport/portability/portability.h>
+
+#include <stdio.h>
+#include <cstdint>
+#include <string>
+
+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 <hicn/transport/errors/runtime_exception.h>
+#include <hicn/transport/portability/portability.h>
+#include <hicn/transport/utils/array.h>
+#include <hicn/transport/utils/crypto_hash_type.h>
+
+extern "C" {
+#include <parc/security/parc_CryptoHash.h>
+};
+
+#include <cstring>
+#include <unordered_map>
+
+namespace utils {
+
+class CryptoHasher;
+
+struct EnumClassHash {
+ template <typename T>
+ std::size_t operator()(T t) const {
+ return static_cast<std::size_t>(t);
+ }
+};
+
+static std::unordered_map<CryptoHashType, std::size_t, EnumClassHash>
+ 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 <typename T>
+ CryptoHash(const T* buffer, std::size_t length, CryptoHashType hash_type) {
+ hash_ = parcCryptoHash_CreateFromArray(
+ static_cast<PARCCryptoHashType>(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 <typename T>
+ utils::Array<T> getDigest() const {
+ return utils::Array<T>(
+ static_cast<T*>(parcBuffer_Overlay(parcCryptoHash_GetDigest(hash_), 0)),
+ parcBuffer_Remaining(parcCryptoHash_GetDigest(hash_)));
+ }
+
+ CryptoHashType getType() {
+ return static_cast<CryptoHashType>(parcCryptoHash_GetDigestType(hash_));
+ }
+
+ template <typename T>
+ 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<bool>(
+ 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 <parc/security/parc_CryptoHashType.h>
+};
+
+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 <hicn/transport/utils/crypto_hash.h>
+
+extern "C" {
+#include <parc/security/parc_CryptoHasher.h>
+};
+
+namespace utils {
+
+class CryptoHasher {
+ public:
+ CryptoHasher(CryptoHashType hash_type)
+ : hasher_(parcCryptoHasher_Create(
+ static_cast<PARCCryptoHashType>(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 <typename T>
+ 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 <parc/security/parc_CryptoSuite.h>
+};
+
+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 <hicn/transport/errors/runtime_exception.h>
+#include <hicn/transport/utils/daemonizator.h>
+#include <hicn/transport/utils/log.h>
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+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 <cstdlib>
+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 <hicn/transport/utils/event_reactor.h>
+
+#include <chrono>
+#include <cstddef>
+#include <cstring>
+#include <utility>
+
+namespace std {
+namespace chrono {
+namespace detail {
+
+template <typename From, typename To>
+struct posix_duration_cast;
+
+// chrono -> timespec caster
+template <typename Rep, typename Period>
+struct posix_duration_cast<std::chrono::duration<Rep, Period>,
+ struct timespec> {
+ static struct timespec cast(std::chrono::duration<Rep, Period> const &d) {
+ struct timespec tv;
+
+ std::chrono::seconds const sec =
+ std::chrono::duration_cast<std::chrono::seconds>(d);
+
+ tv.tv_sec = sec.count();
+ tv.tv_nsec =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(d - sec).count();
+
+ return tv;
+ }
+};
+
+// timespec -> chrono caster
+template <typename Rep, typename Period>
+struct posix_duration_cast<struct timespec,
+ std::chrono::duration<Rep, Period>> {
+ static std::chrono::duration<Rep, Period> cast(struct timespec const &tv) {
+ return std::chrono::duration_cast<std::chrono::duration<Rep, Period>>(
+ std::chrono::seconds(tv.tv_sec) + std::chrono::nanoseconds(tv.tv_nsec));
+ }
+};
+
+} // namespace detail
+
+// chrono -> timespec
+template <typename T, typename Rep, typename Period>
+auto duration_cast(std::chrono::duration<Rep, Period> const &d) ->
+ typename std::enable_if<std::is_same<T, struct timespec>::value,
+ struct timespec>::type {
+ return detail::posix_duration_cast<std::chrono::duration<Rep, Period>,
+ timespec>::cast(d);
+}
+
+// timespec -> chrono
+template <typename Duration>
+Duration duration_cast(struct timespec const &tv) {
+ return detail::posix_duration_cast<struct timespec, Duration>::cast(tv);
+}
+
+} // namespace chrono
+} // namespace std
+
+namespace utils {
+
+template <typename Implementation>
+class DeadlineTimer {
+ public:
+ virtual ~DeadlineTimer() = default;
+
+ template <typename WaitHandler>
+ void asyncWait(WaitHandler &&callback) {
+ static_cast<Implementation *>(this)->asyncWaitImpl(
+ std::forward<WaitHandler>(callback));
+ }
+
+ void wait() { static_cast<Implementation *>(this)->waitImpl(); }
+
+ template <typename T, typename R>
+ void expiresFromNow(std::chrono::duration<T, R> &&duration) {
+ static_cast<Implementation *>(this)->expiresFromNowImpl(
+ std::forward<std::chrono::duration<T, R>>(duration));
+ }
+
+ template <typename TimePoint,
+ typename = typename std::enable_if<
+ std::is_same<std::remove_reference_t<TimePoint>,
+ std::chrono::steady_clock::time_point>::value,
+ TimePoint>::type>
+ void expiresAt(TimePoint &&time_point) {
+ static_cast<Implementation *>(this)->expiresAtImpl(
+ std::forward<TimePoint>(time_point));
+ }
+
+ void cancel() { static_cast<Implementation *>(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 <hicn/transport/portability/portability.h>
+
+#include <arpa/inet.h>
+#include <cstring>
+
+namespace utils {
+
+namespace {
+
+template <size_t Size>
+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<sz / 8> { \
+ 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 <typename T>
+struct EndianInt {
+ static_assert(
+ (std::is_integral<T>::value && !std::is_same<T, bool>::value) ||
+ std::is_floating_point<T>::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<s>::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<t>(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 <typename T>
+ static T swap(T x) {
+ return EndianInt<T>::swap(x);
+ }
+
+ template <typename T>
+ static T big(T x) {
+ return EndianInt<T>::big(x);
+ }
+
+ template <typename T>
+ static T little(T x) {
+ return EndianInt<T>::little(x);
+ }
+
+#if !defined(__ANDROID__)
+ GENERATOR3(64)
+ GENERATOR3(32)
+ GENERATOR3(16)
+ GENERATOR3(8)
+#endif
+};
+
+template <typename T>
+static TRANSPORT_ALWAYS_INLINE T ntoh(T x) {
+ return Endian::order == Endian::Order::LITTLE ? Endian::little(x) : x;
+}
+
+template <typename T>
+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 <hicn/transport/utils/branch_prediction.h>
+#include <hicn/transport/utils/epoll_event_reactor.h>
+#include <hicn/transport/utils/fd_deadline_timer.h>
+
+#include <signal.h>
+#include <unistd.h>
+#include <iostream>
+
+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 <hicn/transport/utils/event_reactor.h>
+
+#include <sys/epoll.h>
+#include <cstddef>
+#include <functional>
+#include <mutex>
+#include <unordered_map>
+
+#define FD_NUMBER 20000
+
+namespace utils {
+
+typedef struct epoll_event Event;
+typedef std::function<int(const Event &)> EventCallback;
+typedef std::unordered_map<int, EventCallback> 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 <cstddef>
+#include <functional>
+#include <system_error>
+
+namespace utils {
+
+typedef std::function<void(const std::error_code &ec)> 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 <hicn/transport/errors/runtime_exception.h>
+#include <memory>
+
+#include <asio.hpp>
+
+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<asio::io_service>()),
+ io_service_(*internal_io_service_),
+ work_(io_service_),
+ thread_(nullptr) {
+ run();
+ }
+
+ ~EventThread() { stop(); }
+
+ void run() {
+ if (stopped()) {
+ io_service_.reset();
+ }
+
+ thread_ = std::make_unique<std::thread>([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 <typename Func>
+ void add(Func&& f) {
+ // If the function f
+ // TODO USe post in mac os, asio->post in xenial
+ io_service_.post(std::forward<Func&&>(f));
+ }
+
+ template <typename Func>
+ void tryRunHandlerNow(Func&& f) {
+ io_service_.dispatch(std::forward<Func&&>(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<asio::io_service> internal_io_service_;
+ asio::io_service& io_service_;
+ asio::io_service::work work_;
+ std::unique_ptr<std::thread> 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 <hicn/transport/errors/runtime_exception.h>
+#include <hicn/transport/utils/deadline_timer.h>
+#include <hicn/transport/utils/epoll_event_reactor.h>
+#include <hicn/transport/utils/log.h>
+
+#include <chrono>
+#include <cstddef>
+
+#include <sys/timerfd.h>
+#include <unistd.h>
+
+namespace utils {
+
+class FdDeadlineTimer : public DeadlineTimer<FdDeadlineTimer> {
+ 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 <typename WaitHandler>
+ 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 <typename T, typename R>
+ void expiresFromNowImpl(std::chrono::duration<T, R> &&duration) {
+ std::memset(&new_value_, 0, sizeof(new_value_));
+ new_value_.it_value = std::chrono::duration_cast<struct timespec>(
+ std::forward<std::chrono::duration<T, R>>(duration));
+ }
+
+ template <typename TimePoint,
+ typename = std::enable_if_t<
+ std::is_same<std::remove_reference_t<TimePoint>,
+ 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<struct timespec>(
+ 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 <hicn/transport/portability/portability.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+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<const signed char *>(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<const signed char *>(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 <hicn/transport/utils/identity.h>
+
+extern "C" {
+#include <parc/security/parc_PublicKeySigner.h>
+#include <parc/security/parc_Security.h>
+}
+
+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<PARCCryptoSuite>(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<int>(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<PARCCryptoSuite>(suite)));
+
+ signer_ = std::make_shared<Signer>(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<PARCCryptoHashType>(hash_algorithm));
+
+ signer_ = std::make_shared<Signer>(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 <hicn/transport/core/manifest_format.h>
+#include <hicn/transport/utils/crypto_suite.h>
+#include <hicn/transport/utils/signer.h>
+
+extern "C" {
+#include <parc/security/parc_Identity.h>
+#include <parc/security/parc_IdentityFile.h>
+#include <parc/security/parc_Pkcs12KeyStore.h>
+};
+
+#include <string>
+
+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> 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 <cstdint>
+#include <utility>
+
+namespace utils {
+
+using KeyId = std::pair<uint8_t*, uint8_t>;
+
+}
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 <hicn/transport/portability/portability.h>
+#include <hicn/transport/utils/log.h>
+
+#include <arpa/inet.h>
+#include <ifaddrs.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <string>
+
+#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 <hicn/transport/portability/portability.h>
+
+#include <cstdint>
+
+TRANSPORT_ALWAYS_INLINE std::uint8_t operator"" _U8(unsigned long long value) {
+ return static_cast<std::uint8_t>(value);
+}
+
+TRANSPORT_ALWAYS_INLINE std::uint16_t operator"" _U16(
+ unsigned long long value) {
+ return static_cast<std::uint16_t>(value);
+}
+
+TRANSPORT_ALWAYS_INLINE std::uint32_t operator"" _U32(
+ unsigned long long value) {
+ return static_cast<std::uint32_t>(value);
+}
+
+TRANSPORT_ALWAYS_INLINE std::uint64_t operator"" _U64(
+ unsigned long long value) {
+ return static_cast<std::uint64_t>(value);
+}
+
+TRANSPORT_ALWAYS_INLINE std::int8_t operator"" _I8(unsigned long long value) {
+ return static_cast<std::int8_t>(value);
+}
+
+TRANSPORT_ALWAYS_INLINE std::int16_t operator"" _I16(unsigned long long value) {
+ return static_cast<std::int16_t>(value);
+}
+
+TRANSPORT_ALWAYS_INLINE std::int32_t operator"" _I32(unsigned long long value) {
+ return static_cast<std::int32_t>(value);
+}
+
+TRANSPORT_ALWAYS_INLINE std::int64_t operator"" _I64(unsigned long long value) {
+ return static_cast<std::int64_t>(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:
+ *
+ * PREFIX<prefix_delimiter>TAG<tag_delimiter>
+ *
+ * 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 <assert.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <hicn/transport/utils/log.h>
+
+#if defined(_WIN32) || defined(_WIN64)
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <sys/time.h>
+#if defined(__linux__)
+#include <linux/limits.h>
+#else
+#include <sys/syslimits.h>
+#endif
+#endif
+
+#if defined(__linux__)
+#include <sys/prctl.h>
+#include <sys/types.h>
+#if !defined(__ANDROID__)
+#include <sys/syscall.h>
+#endif
+#endif
+#if defined(__MACH__)
+#include <pthread.h>
+#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 <android/log.h>
+
+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 <CoreFoundation/CoreFoundation.h>
+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 <windows.h>
+
+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 <hicn/transport_log.h>
+ *
+ * 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 <hicn/transport_log.h>
+ *
+ * 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 <hicn/transport_log.h>
+ * 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 <hicn/transport_log.h>
+ * 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 <hicn/transport_log.h>
+ *
+ * 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 <hicn/transport_log.h>
+ *
+ * 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 <hicn/transport_log.h>
+ *
+ * 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 <hicn/transport_log.h>
+ * 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 <hicn/transport_log.h>
+ *
+ * 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 <hicn/transport/utils/membuf.h>
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <stdexcept>
+#include <vector>
+
+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<uint16_t> 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<HeapStorage*>(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<uint8_t*>(ptr) - offsetof(HeapStorage, buf);
+ auto* storage = reinterpret_cast<HeapStorage*>(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<HeapStorage*>(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> 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> 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<HeapFullStorage*>(malloc(mallocSize));
+
+ new (&storage->hs.prefix) HeapPrefix(kMemBufInUse | kDataInUse);
+ new (&storage->shared) SharedInfo(freeInternalBuf, storage);
+
+ uint8_t* bufAddr = reinterpret_cast<uint8_t*>(&storage->align);
+ uint8_t* storageEnd = reinterpret_cast<uint8_t*>(storage) + mallocSize;
+ size_t actualCapacity = size_t(storageEnd - bufAddr);
+ unique_ptr<MemBuf> ret(new (&storage->hs.buf) MemBuf(
+ InternalConstructor(), packFlagsAndSharedInfo(0, &storage->shared),
+ bufAddr, actualCapacity, bufAddr, 0));
+ return ret;
+}
+
+unique_ptr<MemBuf> MemBuf::createSeparate(std::size_t capacity) {
+ return std::make_unique<MemBuf>(CREATE, capacity);
+}
+
+unique_ptr<MemBuf> MemBuf::createChain(size_t totalCapacity,
+ std::size_t maxBufCapacity) {
+ unique_ptr<MemBuf> out =
+ create(std::min(totalCapacity, size_t(maxBufCapacity)));
+ size_t allocatedCapacity = out->capacity();
+
+ while (allocatedCapacity < totalCapacity) {
+ unique_ptr<MemBuf> 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<uint8_t*>(buf)),
+ buf_(static_cast<uint8_t*>(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> 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<MemBuf>(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<uint8_t*>(const_cast<void*>(buf)), capacity,
+ static_cast<uint8_t*>(const_cast<void*>(buf)), capacity) {}
+
+unique_ptr<MemBuf> MemBuf::wrapBuffer(const void* buf, std::size_t capacity) {
+ return std::make_unique<MemBuf>(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<MemBuf>&& 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> MemBuf::clone() const {
+ return std::make_unique<MemBuf>(cloneAsValue());
+}
+
+unique_ptr<MemBuf> MemBuf::cloneOne() const {
+ return std::make_unique<MemBuf>(cloneOneAsValue());
+}
+
+unique_ptr<MemBuf> MemBuf::cloneCoalesced() const {
+ return std::make_unique<MemBuf>(cloneCoalescedAsValue());
+}
+
+unique_ptr<MemBuf> MemBuf::cloneCoalescedWithHeadroomTailroom(
+ std::size_t new_headroom, std::size_t new_tailroom) const {
+ return std::make_unique<MemBuf>(
+ 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<uint8_t*>(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<uint8_t*>(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<uint8_t*>(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<size_t>(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 <hicn/transport/portability/portability.h>
+#include <hicn/transport/utils/branch_prediction.h>
+
+#include <atomic>
+#include <cassert>
+#include <cinttypes>
+#include <cstddef>
+#include <cstring>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <type_traits>
+#include <vector>
+
+// 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<MemBuf> 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<MemBuf> 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<MemBuf> 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<MemBuf> createChain(size_t totalCapacity,
+ std::size_t maxBufCapacity);
+
+ static std::unique_ptr<MemBuf> 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<MemBuf> 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<MemBuf> 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<MemBuf> 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<MemBuf>&& 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<MemBuf>&& iobuf);
+
+ void appendChain(std::unique_ptr<MemBuf>&& iobuf) {
+ // Just use prependChain() on the next element in our chain
+ next_->prependChain(std::move(iobuf));
+ }
+
+ std::unique_ptr<MemBuf> unlink() {
+ next_->prev_ = prev_;
+ prev_->next_ = next_;
+ prev_ = this;
+ next_ = this;
+ return std::unique_ptr<MemBuf>(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<MemBuf> pop() {
+ MemBuf* next = next_;
+ next_->prev_ = prev_;
+ prev_->next_ = next_;
+ prev_ = this;
+ next_ = this;
+ return std::unique_ptr<MemBuf>((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<MemBuf> 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<MemBuf>(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<MemBuf> 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<MemBuf> 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<MemBuf> 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<MemBuf> 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<struct iovec> 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<struct iovec>* 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<MemBuf> 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<MemBuf> 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<uint32_t> 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<uintptr_t>(info);
+ return flags | uinfo;
+ }
+
+ inline SharedInfo* sharedInfo() const {
+ return reinterpret_cast<SharedInfo*>(flags_and_shared_info_ & ~flag_mask);
+ }
+
+ inline void setSharedInfo(SharedInfo* info) {
+ uintptr_t uinfo = reinterpret_cast<uintptr_t>(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 <class UniquePtr>
+ 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<Pointer>(p));
+ delete this;
+ } catch (...) {
+ abort();
+ }
+ }
+
+ private:
+ Deleter deleter_;
+ };
+
+ static void freeUniquePtrBuffer(void* ptr, void* userData) {
+ static_cast<DeleterBase*>(userData)->dispose(ptr);
+ }
+};
+
+// template <class UniquePtr>
+// typename std::enable_if<
+// detail::IsUniquePtrToSL<UniquePtr>::value,
+// std::unique_ptr<MemBuf>>::type
+// MemBuf::takeOwnership(UniquePtr&& buf, size_t count) {
+// size_t size = count * sizeof(typename UniquePtr::element_type);
+// auto deleter = new UniquePtrDeleter<UniquePtr>(buf.get_deleter());
+// return takeOwnership(
+// buf.release(), size, &MemBuf::freeUniquePtrBuffer, deleter);
+// }
+
+inline std::unique_ptr<MemBuf> 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<MemBuf> 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 <hicn/transport/portability/portability.h>
+#include <hicn/transport/utils/log.h>
+
+#include <deque>
+#include <iostream>
+#include <set>
+#include <type_traits>
+#include <vector>
+
+namespace utils {
+
+template <typename T>
+class MinFilter {
+ public:
+ MinFilter(std::size_t size) : size_(size) {}
+
+ std::size_t size() { return by_arrival_.size(); }
+
+ template <typename R>
+ 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<R>(value)));
+ }
+
+ TRANSPORT_ALWAYS_INLINE const T& begin() { return *by_order_.cbegin(); }
+
+ TRANSPORT_ALWAYS_INLINE const T& rBegin() { return *by_order_.crbegin(); }
+
+ private:
+ std::multiset<T> by_order_;
+ std::deque<typename std::multiset<T>::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 <hicn/transport/utils/spinlock.h>
+
+#include <deque>
+#include <memory>
+#include <mutex>
+
+namespace utils {
+
+template <typename T>
+class ObjectPool {
+ class ObjectDeleter {
+ public:
+ ObjectDeleter(ObjectPool<T> *pool = nullptr) : pool_(pool) {}
+
+ void operator()(T *t) {
+ if (pool_) {
+ pool_->add(t);
+ } else {
+ delete t;
+ }
+ }
+
+ private:
+ ObjectPool<T> *pool_;
+ };
+
+ public:
+ using Ptr = std::unique_ptr<T, ObjectDeleter>;
+
+ ObjectPool() {}
+
+ std::pair<bool, Ptr> get() {
+ if (object_pool_.empty()) {
+ return std::make_pair<bool, Ptr>(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<bool, Ptr>(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<Ptr> 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 <atomic>
+#include <cstddef>
+
+namespace utils {
+
+/**
+ * NOTE: Single consumer single producer ring buffer
+ */
+template <typename Element, std::size_t Size>
+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<std::size_t> tail_; // tail(input) index
+ Element array_[Capacity];
+ std::atomic<std::size_t> head_; // head(output) index
+ std::atomic<std::size_t> size_;
+};
+
+template <typename Element, std::size_t Size>
+bool CircularFifo<Element, Size>::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 <typename Element, std::size_t Size>
+bool CircularFifo<Element, Size>::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 <typename Element, std::size_t Size>
+bool CircularFifo<Element, Size>::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 <typename Element, std::size_t Size>
+bool CircularFifo<Element, Size>::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 <typename Element, std::size_t Size>
+bool CircularFifo<Element, Size>::wasFull() const {
+ const auto next_tail =
+ increment(tail_.load()); // acquire, we dont know who call
+ return (next_tail == head_.load());
+}
+
+template <typename Element, std::size_t Size>
+bool CircularFifo<Element, Size>::isLockFree() const {
+ return (tail_.is_lock_free() && head_.is_lock_free());
+}
+
+template <typename Element, std::size_t Size>
+std::size_t CircularFifo<Element, Size>::increment(std::size_t idx) const {
+ return (idx + 1) % Capacity;
+}
+
+template <typename Element, std::size_t Size>
+std::size_t CircularFifo<Element, Size>::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 <memory>
+#include <vector>
+
+namespace utils {
+
+template <class T>
+class SharableVector : public std::vector<T>,
+ public std::enable_shared_from_this<SharableVector<T>> {
+ 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 <hicn/transport/errors/malformed_ahpacket_exception.h>
+#include <hicn/transport/utils/endianess.h>
+#include <hicn/transport/utils/membuf.h>
+#include <hicn/transport/utils/signer.h>
+#include <hicn/transport/utils/key_id.h>
+
+
+extern "C" {
+TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat")
+#include <hicn/hicn.h>
+#include <parc/security/parc_PublicKeySigner.h>
+#include <parc/security/parc_Security.h>
+#include <parc/security/parc_SymmetricKeySigner.h>
+}
+
+#include <chrono>
+
+#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<milliseconds>(system_clock::now().time_since_epoch()).count();
+ packet.setSignatureTimestamp(now);
+ // *reinterpret_cast<uint64_t*>(ah->signTime) = utils::hton<uint64_t>(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<utils::MemBuf> signature_buffer;
+ std::unique_ptr<utils::MemBuf> 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 <hicn/transport/core/packet.h>
+
+extern "C" {
+#include <parc/security/parc_CryptoHashType.h>
+#include <parc/security/parc_CryptoSuite.h>
+#include <parc/security/parc_KeyStore.h>
+#include <parc/security/parc_Signer.h>
+}
+
+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 <hicn/transport/config.h>
+#include <hicn/transport/core/content_object.h>
+#include <hicn/transport/core/facade.h>
+#include <hicn/transport/core/interest.h>
+#include <hicn/transport/core/manifest_format_fixed.h>
+#include <hicn/transport/core/manifest_inline.h>
+#include <hicn/transport/core/name.h>
+#include <hicn/transport/transport/download_observer.h>
+#include <hicn/transport/transport/socket_options_default_values.h>
+#include <hicn/transport/transport/socket_options_keys.h>
+#include <hicn/transport/utils/crypto_suite.h>
+#include <hicn/transport/utils/identity.h>
+#include <hicn/transport/utils/verifier.h>
+
+#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 <typename PortalType>
+class Socket;
+class ConsumerSocket;
+class ProducerSocket;
+
+using Interest = core::Interest;
+using ContentObject = core::ContentObject;
+using Name = core::Name;
+using ContentObjectManifest = core::ManifestInline<ContentObject, core::Fixed>;
+using InterestManifest = core::ManifestInline<Interest, core::Fixed>;
+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<VPPForwarderPortal>;
+using BasePortal = VPPForwarderPortal;
+#else
+using BaseSocket = Socket<HicnForwarderPortal>;
+using BasePortal = HicnForwarderPortal;
+#endif
+
+using PayloadType = core::PayloadType;
+using Prefix = core::Prefix;
+using Array = utils::Array<uint8_t>;
+
+using ConsumerInterestCallback =
+ std::function<void(ConsumerSocket &, const Interest &)>;
+
+using ConsumerContentCallback =
+ std::function<void(ConsumerSocket &, std::size_t, const std::error_code &)>;
+
+using ConsumerTimerCallback =
+ std::function<void(ConsumerSocket &, std::size_t,
+ std::chrono::milliseconds &, float, uint32_t, uint32_t)>;
+
+using ProducerContentCallback = std::function<void(
+ ProducerSocket &, const std::error_code &, uint64_t bytes_written)>;
+
+using ConsumerContentObjectCallback =
+ std::function<void(ConsumerSocket &, const ContentObject &)>;
+
+using ConsumerContentObjectVerificationCallback =
+ std::function<bool(ConsumerSocket &, const ContentObject &)>;
+
+using ConsumerManifestCallback =
+ std::function<void(ConsumerSocket &, const ContentObjectManifest &)>;
+
+using ProducerContentObjectCallback =
+ std::function<void(ProducerSocket &, ContentObject &)>;
+
+using ProducerInterestCallback =
+ std::function<void(ProducerSocket &, const Interest &)>;
+
+using ProducerInterestCallback =
+ std::function<void(ProducerSocket &, const Interest &)>;
+
+template <typename PortalType>
+class Socket {
+ static_assert(std::is_same<PortalType, HicnForwarderPortal>::value
+#ifdef __linux__
+#ifndef __ANDROID__
+ || std::is_same<PortalType, RawSocketPortal>::value
+#ifdef __vpp__
+ || std::is_same<PortalType, VPPForwarderPortal>::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<Prefix> 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<Prefix> &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<Portal> &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 <atomic>
+
+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 <streambuf>
+
+namespace utils {
+
+template <typename char_type>
+struct ostreambuf
+ : public std::basic_streambuf<char_type, std::char_traits<char_type> > {
+ 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 <hicn/transport/errors/errors.h>
+#include <hicn/transport/utils/string_tokenizer.h>
+
+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 <string>
+
+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 <sstream>
+
+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 <hicn/transport/errors/runtime_exception.h>
+#include <hicn/transport/utils/uri.h>
+
+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 <algorithm> // find
+#include <string>
+
+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 <hicn/transport/core/packet.h>
+#include <hicn/transport/errors/malformed_ahpacket_exception.h>
+#include <hicn/transport/portability/portability.h>
+#include <hicn/transport/utils/key_id.h>
+#include <hicn/transport/utils/log.h>
+#include <hicn/transport/utils/verifier.h>
+
+extern "C" {
+TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat")
+#include <hicn/hicn.h>
+#include <parc/security/parc_CertificateFactory.h>
+#include <parc/security/parc_InMemoryVerifier.h>
+#include <parc/security/parc_Security.h>
+}
+
+#include <sys/stat.h>
+
+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<PARCCryptoSuite>(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 &>(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 <hicn/transport/core/packet.h>
+
+extern "C" {
+#include <parc/security/parc_KeyId.h>
+#include <parc/security/parc_Verifier.h>
+}
+
+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 <hicn/transport/interfaces/socket_consumer.h>
+#include <hicn/transport/interfaces/socket_producer.h>
+#include <hicn/transport/utils/daemonizator.h>
+#include <hicn/transport/utils/literals.h>
+
+#include <fstream>
+#include <iomanip>
+
+#ifdef __linux__
+#include <mcheck.h>
+#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<utils::SharableVector<uint8_t>>()),
+ 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<utils::SharableVector<uint8_t>> 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<std::chrono::steady_clock> 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<TimeDuration>(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<ConsumerSocket>(transport_protocol);
+
+#if defined(DEBUG) && defined(__linux__)
+ std::shared_ptr<transport::BasePortal> portal;
+ consumer_socket_->getSocketOption(GeneralTransportOptions::PORTAL, portal);
+ signals_ =
+ std::make_unique<asio::signal_set>(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<ConsumerSocket> consumer_socket_;
+ Time t1_;
+ uint32_t total_duration_milliseconds_;
+ uint64_t old_bytes_value_;
+ // std::unique_ptr<asio::signal_set> 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<ContentObject>(
+ 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<ContentObjectManifest>(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<const uint8_t *>(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<ProducerSocket>();
+
+ 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<ProducerSocket> producer_socket_;
+ // asio::signal_set signals_;
+ std::vector<std::shared_ptr<ContentObject>> 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<content_size>\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<keystore_path>\t\t\t"
+ << "Path of p12 file containing the "
+ "crypto material used for signing the packets"
+ << std::endl;
+ std::cerr << "-y\t<hash_algorithm>\t\t"
+ << "Use the selected hash algorithm for "
+ "calculating manifest digests"
+ << std::endl;
+ std::cerr << "-p\t<password>\t\t\t"
+ << "Password for p12 keystore" << std::endl;
+ std::cerr << std::endl;
+ std::cerr << "Client specific:" << std::endl;
+ std::cerr << "-b\t<beta_parameter>\t\t"
+ << "RAAQM beta parameter" << std::endl;
+ std::cerr << "-d\t<drop_factor_parameter>\t\t"
+ << "RAAQM drop factor "
+ "parameter"
+ << std::endl;
+ std::cerr << "-W\t<window_size>\t\t\t"
+ << "Use a fixed congestion window "
+ "for retrieving the data."
+ << std::endl;
+ std::cerr << "-c\t<certificate_path>\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 <hicn/transport/interfaces/socket.h>
+#include <hicn/transport/utils/verifier.h>
+
+#include <asio/steady_timer.hpp>
+#include <chrono>
+#include <map>
+
+#define SYN_STATE 1
+#define ACK_STATE 2
+
+namespace transport {
+
+namespace core {
+
+namespace ping {
+
+typedef std::map<uint64_t, uint64_t> 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<std::chrono::microseconds>(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::microseconds>(
+ 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::microseconds>(
+ 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<asio::steady_timer> 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 <val> ping interval in microseconds (default 1000000ms)"
+ << std::endl;
+ std::cout << "-m <val> maximum number of pings to send (default 10)"
+ << std::endl;
+ std::cout << "-s <val> sorce port (default 9695)" << std::endl;
+ std::cout << "-d <val> destination port (default 8080)" << std::endl;
+ std::cout << "-t <val> 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 <val> hicn name (default b001::1)" << std::endl;
+ std::cout
+ << "-l <val> 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<std::chrono::microseconds>(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 <hicn/transport/interfaces/socket_producer.h>
+#include <hicn/transport/utils/daemonizator.h>
+#include <hicn/transport/utils/signer.h>
+#include <hicn/transport/utils/string_tokenizer.h>
+
+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<ContentObject>(
+ 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<std::shared_ptr<ContentObject>> 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 <val> object content size (default 1350B)" << std::endl;
+ std::cout << "-n <val> 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);
+}