66 "fmt"
77 "log/slog"
88 "net/http"
9+ "net/netip"
910
1011 mdmv1 "github.com/metal-stack/masterdata-api/api/v1"
1112 mdm "github.com/metal-stack/masterdata-api/pkg/client"
@@ -205,6 +206,7 @@ func (r *networkResource) findNetworks(request *restful.Request, response *restf
205206 r .send (request , response , http .StatusOK , result )
206207}
207208
209+ // TODO allow creation of networks with childprefixlength which are not privatesuper
208210func (r * networkResource ) createNetwork (request * restful.Request , response * restful.Response ) {
209211 var requestPayload v1.NetworkCreateRequest
210212 err := request .ReadEntity (& requestPayload )
@@ -260,18 +262,34 @@ func (r *networkResource) createNetwork(request *restful.Request, response *rest
260262 }
261263
262264 prefixes := metal.Prefixes {}
263- // all Prefixes must be valid
265+ addressFamilies := make (map [string ]bool )
266+ // all Prefixes must be valid and from the same addressfamily
264267 for i := range requestPayload .Prefixes {
265268 p := requestPayload .Prefixes [i ]
266269 prefix , err := metal .NewPrefixFromCIDR (p )
267270 if err != nil {
268271 r .sendError (request , response , httperrors .BadRequest (fmt .Errorf ("given prefix %v is not a valid ip with mask: %w" , p , err )))
269272 return
270273 }
271-
274+ ipprefix , err := netip .ParsePrefix (p )
275+ if err != nil {
276+ r .sendError (request , response , httperrors .BadRequest (fmt .Errorf ("given prefix %v is not a valid ip with mask: %w" , p , err )))
277+ return
278+ }
279+ if ipprefix .Addr ().Is4 () {
280+ addressFamilies ["ipv4" ] = true
281+ }
282+ if ipprefix .Addr ().Is6 () {
283+ addressFamilies ["ipv6" ] = true
284+ }
272285 prefixes = append (prefixes , * prefix )
273286 }
274287
288+ if len (addressFamilies ) > 1 {
289+ r .sendError (request , response , httperrors .BadRequest (fmt .Errorf ("given prefixes have different addressfamilies" )))
290+ return
291+ }
292+
275293 destPrefixes := metal.Prefixes {}
276294 for i := range requestPayload .DestinationPrefixes {
277295 p := requestPayload .DestinationPrefixes [i ]
@@ -324,17 +342,38 @@ func (r *networkResource) createNetwork(request *restful.Request, response *rest
324342 return
325343 }
326344
345+ // We should support two private super per partition, one per addressfamily
346+ // the network allocate request must be configurable with addressfamily
327347 if privateSuper {
328348 boolTrue := true
329- err := r .ds .FindNetwork (& datastore.NetworkSearchQuery {PartitionID : & partition .ID , PrivateSuper : & boolTrue }, & metal.Network {})
349+ var nw metal.Network
350+ err := r .ds .FindNetwork (& datastore.NetworkSearchQuery {PartitionID : & partition .ID , PrivateSuper : & boolTrue }, & nw )
330351 if err != nil {
331352 if ! metal .IsNotFound (err ) {
332353 r .sendError (request , response , defaultError (err ))
333354 return
334355 }
335356 } else {
336- r .sendError (request , response , defaultError (fmt .Errorf ("partition with id %q already has a private super network" , partition .ID )))
337- return
357+ if len (nw .Prefixes ) != 0 {
358+ existingsuper := nw .Prefixes [0 ].String ()
359+
360+ ipprefxexistingsuper , err := netip .ParsePrefix (existingsuper )
361+ if err != nil {
362+ r .sendError (request , response , httperrors .BadRequest (fmt .Errorf ("given prefix %v is not valid: %w" , existingsuper , err )))
363+ return
364+
365+ }
366+ newsuper := prefixes [0 ].String ()
367+ ipprefixnew , err := netip .ParsePrefix (newsuper )
368+ if err != nil {
369+ r .sendError (request , response , httperrors .BadRequest (fmt .Errorf ("given prefix %v is not valid: %w" , newsuper , err )))
370+ return
371+ }
372+ if (ipprefixnew .Addr ().Is4 () && ipprefxexistingsuper .Addr ().Is4 ()) || (ipprefixnew .Addr ().Is6 () && ipprefxexistingsuper .Addr ().Is6 ()) {
373+ r .sendError (request , response , httperrors .BadRequest (fmt .Errorf ("partition with id %q already has a private super network for this addressfamily" , partition .ID )))
374+ return
375+ }
376+ }
338377 }
339378 }
340379 if underlay {
@@ -389,6 +428,23 @@ func (r *networkResource) createNetwork(request *restful.Request, response *rest
389428 Labels : labels ,
390429 }
391430
431+ // check if childprefixlength is set and matches addressfamily
432+ if requestPayload .ChildPrefixLength != nil && privateSuper {
433+ cpl := * requestPayload .ChildPrefixLength
434+ for _ , p := range prefixes {
435+ ipprefix , err := netip .ParsePrefix (p .String ())
436+ if err != nil {
437+ r .sendError (request , response , httperrors .BadRequest (fmt .Errorf ("given prefix %v is not a valid ip with mask: %w" , p , err )))
438+ return
439+ }
440+ if cpl <= uint8 (ipprefix .Bits ()) {
441+ r .sendError (request , response , httperrors .BadRequest (fmt .Errorf ("given childprefixlength %d is not greater than prefix length of:%s" , cpl , p .String ())))
442+ return
443+ }
444+ }
445+ nw .ChildPrefixLength = requestPayload .ChildPrefixLength
446+ }
447+
392448 ctx := request .Request .Context ()
393449 for _ , p := range nw .Prefixes {
394450 err := r .ipamer .CreatePrefix (ctx , p )
@@ -409,6 +465,7 @@ func (r *networkResource) createNetwork(request *restful.Request, response *rest
409465 r .send (request , response , http .StatusCreated , v1 .NewNetworkResponse (nw , usage ))
410466}
411467
468+ // TODO add possibility to allocate from a non super network if given in the AllocateRequest and super has childprefixlength
412469func (r * networkResource ) allocateNetwork (request * restful.Request , response * restful.Response ) {
413470 var requestPayload v1.NetworkAllocateRequest
414471 err := request .ReadEntity (& requestPayload )
@@ -463,9 +520,9 @@ func (r *networkResource) allocateNetwork(request *restful.Request, response *re
463520 return
464521 }
465522
466- var superNetwork metal.Network
523+ var superNetworks metal.Networks
467524 boolTrue := true
468- err = r .ds .FindNetwork (& datastore.NetworkSearchQuery {PartitionID : & partition .ID , PrivateSuper : & boolTrue }, & superNetwork )
525+ err = r .ds .SearchNetworks (& datastore.NetworkSearchQuery {PartitionID : & partition .ID , PrivateSuper : & boolTrue }, & superNetworks )
469526 if err != nil {
470527 r .sendError (request , response , defaultError (err ))
471528 return
@@ -482,6 +539,37 @@ func (r *networkResource) allocateNetwork(request *restful.Request, response *re
482539 destPrefixes = append (destPrefixes , * prefix )
483540 }
484541
542+ addressFamily := v1 .IPv4AddressFamily
543+ if requestPayload .AddressFamily != nil {
544+ addressFamily = v1 .ToAddressFamily (* requestPayload .AddressFamily )
545+ }
546+
547+ r .log .Info ("network allocate" , "family" , addressFamily )
548+ var (
549+ superNetwork metal.Network
550+ superNetworkFound bool
551+ )
552+ for _ , snw := range superNetworks {
553+ ipprefix , err := netip .ParsePrefix (snw .Prefixes [0 ].String ())
554+ if err != nil {
555+ r .sendError (request , response , httperrors .BadRequest (err ))
556+ return
557+ }
558+ if addressFamily == v1 .IPv4AddressFamily && ipprefix .Addr ().Is4 () {
559+ superNetwork = snw
560+ superNetworkFound = true
561+ }
562+ if addressFamily == v1 .IPv6AddressFamily && ipprefix .Addr ().Is6 () {
563+ superNetwork = snw
564+ superNetworkFound = true
565+ }
566+ }
567+ if ! superNetworkFound {
568+ r .sendError (request , response , httperrors .BadRequest (fmt .Errorf ("no supernetwork for addressfamily:%s found" , addressFamily )))
569+ return
570+ }
571+ r .log .Info ("network allocate" , "supernetwork" , superNetwork .ID )
572+
485573 nwSpec := & metal.Network {
486574 Base : metal.Base {
487575 Name : name ,
@@ -494,8 +582,18 @@ func (r *networkResource) allocateNetwork(request *restful.Request, response *re
494582 Shared : shared ,
495583 Nat : nat ,
496584 }
585+ if superNetwork .ChildPrefixLength == nil {
586+ r .sendError (request , response , httperrors .BadRequest (fmt .Errorf ("supernetwork %s has no childprefixlength specified" , superNetwork .ID )))
587+ }
588+
589+ // Allow configurable prefix length
590+ length := * superNetwork .ChildPrefixLength
591+ if requestPayload .Length != nil {
592+ length = * requestPayload .Length
593+ }
594+
497595 ctx := request .Request .Context ()
498- nw , err := createChildNetwork (ctx , r .ds , r .ipamer , nwSpec , & superNetwork , partition . PrivateNetworkPrefixLength )
596+ nw , err := createChildNetwork (ctx , r .ds , r .ipamer , nwSpec , & superNetwork , length )
499597 if err != nil {
500598 r .sendError (request , response , defaultError (err ))
501599 return
0 commit comments