From 570a75410197226e0985dfacddd69012acb14368 Mon Sep 17 00:00:00 2001 From: Louis Royer Date: Fri, 21 Jun 2024 10:44:04 +0200 Subject: [PATCH] wip --- internal/ctrl/rules-registry.go | 15 +++++++ internal/netfunc/headend-encaps-ctrl.go | 60 +++++++++++++++++++++++++ internal/netfunc/packet.go | 12 +++++ 3 files changed, 87 insertions(+) diff --git a/internal/ctrl/rules-registry.go b/internal/ctrl/rules-registry.go index 426e236..9f5a0cc 100644 --- a/internal/ctrl/rules-registry.go +++ b/internal/ctrl/rules-registry.go @@ -7,6 +7,7 @@ package ctrl import ( "fmt" "net/http" + "net/netip" "sync" "github.com/gin-gonic/gin" @@ -26,6 +27,20 @@ func NewRulesRegistry() *RulesRegistry { } } +func (rr *RulesRegistry) Action(dstIp netip.Addr) (jsonapi.Action, error) { + rr.RLock() + defer rr.RUnlock() + for _, r := range rr.rules { + if !r.Enabled { + continue + } + if r.Match.DstIpPrefix.Contains(dstIp) { + return r.Action, nil + } + } + return jsonapi.Action{}, fmt.Errorf("Not found") +} + func (rr *RulesRegistry) GetRule(c *gin.Context) { id := c.Param("uuid") iduuid, err := uuid.FromString(id) diff --git a/internal/netfunc/headend-encaps-ctrl.go b/internal/netfunc/headend-encaps-ctrl.go index a1191ad..70d35da 100644 --- a/internal/netfunc/headend-encaps-ctrl.go +++ b/internal/netfunc/headend-encaps-ctrl.go @@ -6,6 +6,10 @@ package netfunc import ( "fmt" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + gopacket_srv6 "github.com/nextmn/gopacket-srv6" + "net" "net/netip" "github.com/nextmn/srv6/internal/ctrl" @@ -25,5 +29,61 @@ func NewHeadendEncapsWithCtrl(prefix netip.Prefix, rr *ctrl.RulesRegistry, ttl u // Handle a packet func (h HeadendEncapsWithCtrl) Handle(packet []byte) ([]byte, error) { + pqt, err := NewIPv4Packet(packet) + if err != nil { + return nil, err + } + if err := h.CheckDAInPrefixRange(pqt); err != nil { + return nil, err + } + action, err := pqt.Action(h.RulesRegistry) + if err != nil { + return nil, err + } + src := net.ParseIP("::") // FIXME: don't hardcode + nextHop := net.ParseIP(action.SRH) // FIXME: allow multiple segments + ipheader := &layers.IPv6{ + SrcIP: src, + // S06. Set the IPv6 DA = B + DstIP: nextHop, + Version: 6, + NextHeader: layers.IPProtocolIPv6Routing, // IPv6-Route + HopLimit: h.HopLimit(), + // TODO: Generate a FlowLabel with hash(IPv6SA + IPv6DA + policy) + TrafficClass: 0, // FIXME: put this in Action + } + // FIXME: allow multiple segments + //segList := append([]net.IP{seg0}, bsid.ReverseSegmentsList()...) + segList := []net.IP{nextHop} + srh := &gopacket_srv6.IPv6Routing{ + RoutingType: 4, + // the first item on segments list is the next endpoint + SegmentsLeft: uint8(len(segList) - 1), // pointer to next segment + SourceRoutingIPs: segList, + Tag: 0, // not used + Flags: 0, // no flag defined + GopacketIpv6ExtensionBase: gopacket_srv6.GopacketIpv6ExtensionBase{ + NextHeader: layers.IPProtocolIPv4, + }, + } + + // Encapsulate the packet into a new IPv6 header + buf := gopacket.NewSerializeBuffer() + if err := gopacket.SerializeLayers(buf, + gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + }, + ipheader, + srh, + gopacket.Payload(pqt.Packet.Layers()[0].LayerContents()), + gopacket.Payload(pqt.Packet.Layers()[0].LayerPayload()), + ); err != nil { + return nil, err + } else { + // Forward along the shortest path to B + return buf.Bytes(), nil + } + return nil, fmt.Errorf("Not yet implemented") } diff --git a/internal/netfunc/packet.go b/internal/netfunc/packet.go index 239440b..7608384 100644 --- a/internal/netfunc/packet.go +++ b/internal/netfunc/packet.go @@ -11,7 +11,9 @@ import ( "github.com/google/gopacket" "github.com/google/gopacket/layers" + json_api "github.com/nextmn/json-api/jsonapi" "github.com/nextmn/srv6/internal/constants" + "github.com/nextmn/srv6/internal/ctrl" ) type Packet struct { @@ -72,6 +74,16 @@ func (p *Packet) CheckDAInPrefixRange(prefix netip.Prefix) error { return nil } +// Returns the Action related to this packet +func (p *Packet) Action(rr *ctrl.RulesRegistry) (json_api.Action, error) { + dstSlice := p.NetworkLayer().NetworkFlow().Dst().Raw() + dst, ok := netip.AddrFromSlice(dstSlice) + if !ok { + return json_api.Action{}, fmt.Errorf("Malformed packet") + } + return rr.Action(dst) +} + // Returns the first gopacket.Layer after IPv6 header / extension headers func (p *Packet) PopIPv6Headers() (gopacket.Layer, error) { if p.firstLayerType != layers.LayerTypeIPv6 {