Skip to content

Commit aac588b

Browse files
safchaindvandra
authored andcommitted
ondemand: allow to pass specific resource per node
use the mechanism to generate specific ICMP ID or source Port per node.
1 parent 3faaf34 commit aac588b

File tree

9 files changed

+149
-48
lines changed

9 files changed

+149
-48
lines changed

api/server/packet_injector.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func (pi *PacketInjectorAPI) validateRequest(ppr *types.PacketInjection) error {
7474
}
7575

7676
ipField := "IPV4"
77-
if ppr.Type == "icmp6" || ppr.Type == "tcp6" || ppr.Type == "udp6" {
77+
if ppr.Type == types.PiTypeICMP6 || ppr.Type == types.PiTypeTCP6 || ppr.Type == types.PiTypeUDP6 {
7878
ipField = "IPV6"
7979
}
8080

api/types/types.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,26 @@ func (n *NodeRule) Validate() error {
237237
return nil
238238
}
239239

240+
const (
241+
// PIModeUniqPerNode use a unique packet identifier per source node
242+
PIModeUniqPerNode = iota
243+
// PIModeRandom use random packet identifier for each packet
244+
PIModeRandom
245+
246+
// PiTypeICMP4 icmpv4 packet
247+
PiTypeICMP4 = "icmp4"
248+
// PiTypeICMP6 icmpv6 packet
249+
PiTypeICMP6 = "icmp6"
250+
// PiTypeTCP4 ipv4 + tcp packet
251+
PiTypeTCP4 = "tcp4"
252+
// PiTypeTCP6 ipv6 + tcp packet
253+
PiTypeTCP6 = "tcp6"
254+
// PiTypeUDP4 ipv4 + udp packet
255+
PiTypeUDP4 = "udp4"
256+
// PiTypeUDP6 ipv6 + udp packet
257+
PiTypeUDP6 = "udp6"
258+
)
259+
240260
// PacketInjection packet injector API parameters
241261
// easyjson:json
242262
// swagger:model
@@ -256,7 +276,7 @@ type PacketInjection struct {
256276
ICMPID uint16 `yaml:"ICMPID"`
257277
Count uint64 `yaml:"Count"`
258278
Interval uint64 `yaml:"Interval"`
259-
Increment bool `yaml:"Increment"`
279+
Mode int `yaml:"Mode"`
260280
IncrementPayload int64 `yaml:"IncrementPayload"`
261281
StartTime time.Time
262282
Pcap []byte `yaml:"Pcap"`
@@ -270,7 +290,14 @@ func (pi *PacketInjection) GetName() string {
270290

271291
// Validate verifies the packet injection type is supported
272292
func (pi *PacketInjection) Validate() error {
273-
allowedTypes := map[string]bool{"icmp4": true, "icmp6": true, "tcp4": true, "tcp6": true, "udp4": true, "udp6": true}
293+
allowedTypes := map[string]bool{
294+
PiTypeICMP4: true,
295+
PiTypeICMP6: true,
296+
PiTypeTCP4: true,
297+
PiTypeTCP6: true,
298+
PiTypeUDP4: true,
299+
PiTypeUDP6: true,
300+
}
274301
if _, ok := allowedTypes[pi.Type]; !ok {
275302
return errors.New("given type is not supported")
276303
}

cmd/client/packet_injector.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ var PacketInjectionCreate = &cobra.Command{
7373
ICMPID: request.ICMPID,
7474
Count: request.Count,
7575
Interval: request.Interval,
76-
Increment: request.Increment,
76+
Mode: request.Mode,
7777
IncrementPayload: request.IncrementPayload,
7878
TTL: request.TTL,
7979
}

cmd/injector/standalone.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"os"
2525
"time"
2626

27+
"github.com/skydive-project/skydive/api/types"
2728
"github.com/skydive-project/skydive/common"
2829
"github.com/skydive-project/skydive/packetinjector"
2930
pi "github.com/skydive-project/skydive/packetinjector"
@@ -46,25 +47,25 @@ var (
4647
id uint16
4748
count uint64
4849
interval uint64
49-
increment bool
50+
mode int
5051
incrementPayload int64
5152
ttl uint8
5253
)
5354

5455
// AddInjectPacketInjectFlags add the command line flags for a packet injection
5556
func AddInjectPacketInjectFlags(cmd *cobra.Command) {
56-
cmd.Flags().StringVarP(&srcIP, "srcIP", "", "", "source node IP")
57-
cmd.Flags().StringVarP(&dstIP, "dstIP", "", "", "destination node IP")
58-
cmd.Flags().StringVarP(&srcMAC, "srcMAC", "", "", "source node MAC")
59-
cmd.Flags().StringVarP(&dstMAC, "dstMAC", "", "", "destination node MAC")
60-
cmd.Flags().Uint16VarP(&srcPort, "srcPort", "", 0, "source port for TCP packet")
61-
cmd.Flags().Uint16VarP(&dstPort, "dstPort", "", 0, "destination port for TCP packet")
57+
cmd.Flags().StringVarP(&srcIP, "src-ip", "", "", "source node IP")
58+
cmd.Flags().StringVarP(&dstIP, "dst-ip", "", "", "destination node IP")
59+
cmd.Flags().StringVarP(&srcMAC, "src-mac", "", "", "source node MAC")
60+
cmd.Flags().StringVarP(&dstMAC, "dst-mac", "", "", "destination node MAC")
61+
cmd.Flags().Uint16VarP(&srcPort, "src-port", "", 0, "source port for TCP packet")
62+
cmd.Flags().Uint16VarP(&dstPort, "dst-port", "", 0, "destination port for TCP packet")
6263
cmd.Flags().StringVarP(&packetType, "type", "", "icmp4", "packet type: icmp4, icmp6, tcp4, tcp6, udp4 and udp6")
6364
cmd.Flags().StringVarP(&payload, "payload", "", "", "payload")
6465
cmd.Flags().StringVar(&pcap, "pcap", "", "PCAP file")
6566
cmd.Flags().Uint16VarP(&id, "id", "", 0, "ICMP identification")
66-
cmd.Flags().BoolVarP(&increment, "increment", "", false, "increment ICMP id for each packet")
67-
cmd.Flags().Int64VarP(&incrementPayload, "incrementPayload", "", 0, "increase payload for each packet")
67+
cmd.Flags().IntVarP(&mode, "mode", "", types.PIModeRandom, "specify type of modification between injections")
68+
cmd.Flags().Int64VarP(&incrementPayload, "inc-payload", "", 0, "increase payload each packet")
6869
cmd.Flags().Uint64VarP(&count, "count", "", 1, "number of packets to be generated")
6970
cmd.Flags().Uint64VarP(&interval, "interval", "", 0, "wait interval milliseconds between sending each packet")
7071
cmd.Flags().Uint8VarP(&ttl, "ttl", "", 64, "IP time-to-live header")
@@ -110,7 +111,7 @@ func GetPacketInjectRequest() (*pi.PacketInjectionRequest, error) {
110111
Count: count,
111112
ICMPID: id,
112113
Interval: interval,
113-
Increment: increment,
114+
Mode: mode,
114115
IncrementPayload: incrementPayload,
115116
Payload: payload,
116117
Pcap: pcapContent,

flow/ondemand/client/client.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,33 @@ func (h *onDemandFlowHandler) ResourceName() string {
6969
return "Capture"
7070
}
7171

72-
func (h *onDemandFlowHandler) GetNodes(resource types.Resource) []interface{} {
72+
func (h *onDemandFlowHandler) GetNodeResources(resource types.Resource) []client.OnDemandNodeResource {
73+
var nrs []client.OnDemandNodeResource
74+
7375
capture := resource.(*types.Capture)
76+
7477
query := capture.GremlinQuery
7578
query += fmt.Sprintf(".Dedup().Has('Captures.ID', NEE('%s'))", resource.ID())
7679
if capture.Type != "" && !common.CheckProbeCapabilities(capture.Type, common.MultipleOnSameNodeCapability) {
7780
query += fmt.Sprintf(".Has('Captures.Type', NEE('%s'))", capture.Type)
7881
}
7982
query += h.nodeTypeQuery
80-
return h.applyGremlinExpr(query)
83+
84+
if nodes := h.applyGremlinExpr(query); len(nodes) > 0 {
85+
for _, i := range nodes {
86+
switch i.(type) {
87+
case *graph.Node:
88+
nrs = append(nrs, client.OnDemandNodeResource{Node: i.(*graph.Node), Resource: capture})
89+
case []*graph.Node:
90+
// case of shortestpath that returns a list of nodes
91+
for _, node := range i.([]*graph.Node) {
92+
nrs = append(nrs, client.OnDemandNodeResource{Node: node, Resource: capture})
93+
}
94+
}
95+
}
96+
}
97+
98+
return nrs
8199
}
82100

83101
func (h *onDemandFlowHandler) applyGremlinExpr(query string) []interface{} {

ondemand/client/client.go

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,15 @@ import (
3636
ws "github.com/skydive-project/skydive/websocket"
3737
)
3838

39+
type OnDemandNodeResource struct {
40+
Node *graph.Node
41+
Resource types.Resource
42+
}
43+
3944
// OnDemandClientHandler is the interface to be implemented by ondemand clients
4045
type OnDemandClientHandler interface {
4146
ResourceName() string
42-
GetNodes(resource types.Resource) []interface{}
47+
GetNodeResources(resource types.Resource) []OnDemandNodeResource
4348
CheckState(n *graph.Node, resource types.Resource) bool
4449
DecodeMessage(msg json.RawMessage) (types.Resource, error)
4550
EncodeMessage(nodeID graph.Identifier, resource types.Resource) (json.RawMessage, error)
@@ -180,25 +185,28 @@ func (o *OnDemandClient) unregisterTask(node *graph.Node, resource types.Resourc
180185
return true
181186
}
182187

183-
func (o *OnDemandClient) nodeTasks(nodes []interface{}, resource types.Resource) map[graph.Identifier]nodeTask {
184-
toRegister := func(node *graph.Node) (nodeID graph.Identifier, host string, register bool) {
188+
func (o *OnDemandClient) nodeTasks(nrs []OnDemandNodeResource) map[graph.Identifier]nodeTask {
189+
toRegister := func(nr OnDemandNodeResource) (nodeID graph.Identifier, host string, register bool) {
185190
// check not already registered
186-
tasks, ok := o.registeredNodes[node.ID]
191+
tasks, ok := o.registeredNodes[nr.Node.ID]
187192
if ok {
188-
ok = tasks[resource.ID()]
193+
ok = tasks[nr.Resource.ID()]
189194
}
190195

191196
if ok {
192-
logging.GetLogger().Debugf("%s already registered on %s", resource.ID(), node.ID)
197+
logging.GetLogger().Debugf("%s already registered on %s", nr.Resource.ID(), nr.Node.ID)
193198
return
194199
}
195200

196-
return node.ID, node.Host, true
201+
return nr.Node.ID, nr.Node.Host, true
197202
}
198203

199204
nps := map[graph.Identifier]nodeTask{}
200-
for _, i := range nodes {
201-
switch i.(type) {
205+
for _, nr := range nrs {
206+
if nodeID, host, ok := toRegister(nr); ok {
207+
nps[nodeID] = nodeTask{nodeID, host, nr.Resource}
208+
}
209+
/*switch i.(type) {
202210
case *graph.Node:
203211
node := i.(*graph.Node)
204212
if nodeID, host, ok := toRegister(node); ok {
@@ -211,7 +219,7 @@ func (o *OnDemandClient) nodeTasks(nodes []interface{}, resource types.Resource)
211219
nps[nodeID] = nodeTask{nodeID, host, resource}
212220
}
213221
}
214-
}
222+
}*/
215223
}
216224

217225
return nps
@@ -231,8 +239,8 @@ func (o *OnDemandClient) checkForRegistrationCallback() {
231239
defer o.RUnlock()
232240

233241
for _, resource := range o.resources {
234-
if nodes := o.handler.GetNodes(resource); len(nodes) > 0 {
235-
if nps := o.nodeTasks(nodes, resource); len(nps) > 0 {
242+
if nrs := o.handler.GetNodeResources(resource); len(nrs) > 0 {
243+
if nps := o.nodeTasks(nrs); len(nps) > 0 {
236244
go o.registerTasks(nps)
237245
}
238246
}
@@ -312,8 +320,8 @@ func (o *OnDemandClient) registerResource(resource types.Resource) {
312320

313321
o.resources[resource.ID()] = resource
314322

315-
if nodes := o.handler.GetNodes(resource); len(nodes) > 0 {
316-
if nps := o.nodeTasks(nodes, resource); len(nps) > 0 {
323+
if nrs := o.handler.GetNodeResources(resource); len(nrs) > 0 {
324+
if nps := o.nodeTasks(nrs); len(nps) > 0 {
317325
go o.registerTasks(nps)
318326
}
319327
}

packetinjector/client.go

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func (h *onDemandPacketInjectionHandler) createRequest(nodeID graph.Identifier,
105105
Count: pi.Count,
106106
Interval: pi.Interval,
107107
ICMPID: pi.ICMPID,
108-
Increment: pi.Increment,
108+
Mode: pi.Mode,
109109
IncrementPayload: pi.IncrementPayload,
110110
TTL: pi.TTL,
111111
}
@@ -228,10 +228,49 @@ func (h *onDemandPacketInjectionHandler) ResourceName() string {
228228
return "PacketInjection"
229229
}
230230

231-
func (h *onDemandPacketInjectionHandler) GetNodes(resource types.Resource) []interface{} {
232-
query := resource.(*types.PacketInjection).Src
231+
func (h *onDemandPacketInjectionHandler) GetNodeResources(resource types.Resource) []client.OnDemandNodeResource {
232+
var nrs []client.OnDemandNodeResource
233+
234+
pi := resource.(*types.PacketInjection)
235+
236+
query := pi.Src
233237
query += fmt.Sprintf(".Dedup('TID').Has('PacketInjections.ID', NEE('%s'))", resource.ID())
234-
return h.applyGremlinExpr(query)
238+
239+
if nodes := h.applyGremlinExpr(query); len(nodes) > 0 {
240+
id := pi.ICMPID
241+
srcPort := pi.SrcPort
242+
243+
addNrs := func(n *graph.Node) {
244+
r := *pi
245+
r.ICMPID = id
246+
r.SrcPort = srcPort
247+
248+
nrs = append(nrs, client.OnDemandNodeResource{Node: n, Resource: &r})
249+
250+
if r.Mode == types.PIModeUniqPerNode {
251+
switch r.Type {
252+
case types.PiTypeICMP4, types.PiTypeICMP6:
253+
id++
254+
default:
255+
srcPort++
256+
}
257+
}
258+
}
259+
260+
for _, i := range nodes {
261+
switch i.(type) {
262+
case *graph.Node:
263+
addNrs(i.(*graph.Node))
264+
case []*graph.Node:
265+
// case of shortestpath that returns a list of nodes
266+
for _, node := range i.([]*graph.Node) {
267+
addNrs(node)
268+
}
269+
}
270+
}
271+
}
272+
273+
return nrs
235274
}
236275

237276
func (h *onDemandPacketInjectionHandler) applyGremlinExpr(query string) []interface{} {

packetinjector/forge.go

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@ package packetinjector
1919

2020
import (
2121
"fmt"
22+
"math"
2223
"math/rand"
2324
"net"
24-
"strings"
2525
"time"
2626

2727
"github.com/google/gopacket"
2828
"github.com/google/gopacket/layers"
29+
"github.com/skydive-project/skydive/api/types"
2930
"github.com/skydive-project/skydive/common"
3031
"github.com/skydive-project/skydive/flow"
3132
"github.com/skydive-project/skydive/logging"
@@ -47,7 +48,7 @@ type ForgedPacketGenerator struct {
4748
close chan bool
4849
}
4950

50-
func forgePacket(packetType string, layerType gopacket.LayerType, srcMAC, dstMAC net.HardwareAddr, TTL uint8, srcIP, dstIP net.IP, srcPort, dstPort uint16, ID uint64, data string) ([]byte, gopacket.Packet, error) {
51+
func forgePacket(packetType string, layerType gopacket.LayerType, srcMAC, dstMAC net.HardwareAddr, TTL uint8, srcIP, dstIP net.IP, srcPort, dstPort uint16, ID uint16, data string) ([]byte, gopacket.Packet, error) {
5152
var l []gopacket.SerializableLayer
5253

5354
payload := gopacket.Payload([]byte(data))
@@ -68,19 +69,19 @@ func forgePacket(packetType string, layerType gopacket.LayerType, srcMAC, dstMAC
6869
ipLayer := &layers.IPv4{Version: 4, SrcIP: srcIP, DstIP: dstIP, Protocol: layers.IPProtocolICMPv4, TTL: TTL}
6970
icmpLayer := &layers.ICMPv4{
7071
TypeCode: layers.CreateICMPv4TypeCode(layers.ICMPv4TypeEchoRequest, 0),
71-
Id: uint16(ID),
72+
Id: ID,
7273
}
7374
l = append(l, ipLayer, icmpLayer)
7475
case "icmp6":
7576
ipLayer := &layers.IPv6{Version: 6, SrcIP: srcIP, DstIP: dstIP, NextHeader: layers.IPProtocolICMPv6}
7677
icmpLayer := &layers.ICMPv6{
7778
TypeCode: layers.CreateICMPv6TypeCode(layers.ICMPv6TypeEchoRequest, 0),
78-
TypeBytes: []byte{byte(ID & uint64(0xFF00) >> 8), byte(ID & uint64(0xFF)), 0, 0},
79+
TypeBytes: []byte{byte(ID & uint16(0xFF00) >> 8), byte(ID & uint16(0xFF)), 0, 0},
7980
}
8081
icmpLayer.SetNetworkLayerForChecksum(ipLayer)
8182

8283
echoLayer := &layers.ICMPv6Echo{
83-
Identifier: uint16(ID),
84+
Identifier: ID,
8485
}
8586
l = append(l, ipLayer, icmpLayer, echoLayer)
8687
case "tcp4":
@@ -147,22 +148,29 @@ func (f *ForgedPacketGenerator) PacketSource() chan *Packet {
147148
f.Count = 1
148149
}
149150

150-
for i := uint64(0); i < f.Count; i++ {
151-
id := uint64(f.ICMPID)
152-
if strings.HasPrefix(f.Type, "icmp") && f.Increment {
153-
id += i
154-
}
151+
id := f.ICMPID
152+
srcPort := f.SrcPort
155153

156-
if f.IncrementPayload > 0 {
157-
payload = payload + common.RandString(int(f.IncrementPayload))
154+
for i := uint64(0); i < f.Count; i++ {
155+
if f.Mode == types.PIModeRandom {
156+
switch f.Type {
157+
case types.PiTypeICMP4, types.PiTypeICMP6:
158+
id = uint16(rand.Intn(math.MaxUint16-1) + 1)
159+
default:
160+
srcPort = uint16(rand.Intn(math.MaxUint16-1) + 1)
161+
}
158162
}
159163

160-
packetData, packet, err := forgePacket(f.Type, f.LayerType, f.SrcMAC, f.DstMAC, f.TTL, f.SrcIP, f.DstIP, f.SrcPort, f.DstPort, id, payload)
164+
packetData, packet, err := forgePacket(f.Type, f.LayerType, f.SrcMAC, f.DstMAC, f.TTL, f.SrcIP, f.DstIP, srcPort, f.DstPort, id, payload)
161165
if err != nil {
162166
logging.GetLogger().Error(err)
163167
return
164168
}
165169

170+
if f.IncrementPayload > 0 {
171+
payload = payload + common.RandString(int(f.IncrementPayload))
172+
}
173+
166174
select {
167175
case <-f.close:
168176
return

packetinjector/injector.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ type PacketInjectionRequest struct {
4444
Count uint64 `valid:"min=1"`
4545
ICMPID uint16
4646
Interval uint64
47-
Increment bool
47+
Mode int
4848
IncrementPayload int64
4949
Payload string
5050
Pcap []byte

0 commit comments

Comments
 (0)