@@ -12,7 +12,8 @@ use chrono::DateTime;
1212use chrono:: Utc ;
1313use db_macros:: Resource ;
1414use diesel:: AsChangeset ;
15- use ipnetwork:: IpNetwork ;
15+ use itertools:: Either ;
16+ use itertools:: Itertools ;
1617use nexus_db_schema:: schema:: instance_network_interface;
1718use nexus_db_schema:: schema:: network_interface;
1819use nexus_db_schema:: schema:: service_network_interface;
@@ -98,9 +99,13 @@ pub struct NetworkInterface {
9899 /// True if this is the instance's primary interface.
99100 #[ diesel( column_name = is_primary) ]
100101 pub primary : bool ,
101- /// List of additional networks on which the instance is allowed to send /
102- /// receive traffic.
103- pub transit_ips : Vec < IpNetwork > ,
102+ /// List of additional IPv4 networks on which the instance is allowed to
103+ /// send / receive traffic.
104+ #[ diesel( column_name = transit_ips) ]
105+ pub transit_ips_v4 : Vec < crate :: Ipv4Net > ,
106+ /// List of additional IPv6 networks on which the instance is allowed to
107+ /// send / receive traffic.
108+ pub transit_ips_v6 : Vec < crate :: Ipv6Net > ,
104109}
105110
106111impl NetworkInterface {
@@ -119,43 +124,25 @@ impl NetworkInterface {
119124 ) ) ) ;
120125 }
121126 ( None , Some ( ip) ) => {
122- // Check that all transit IPs are IPv6.
123127 let transit_ips = self
124- . transit_ips
128+ . transit_ips_v6
125129 . iter ( )
126- . map ( |net| {
127- let IpNetwork :: V6 ( net) = net else {
128- return Err ( Error :: internal_error ( & format ! (
129- "NIC with ID '{}' is IPv6-only, but has \
130- IPv4 transit IPs",
131- self . id( ) ,
132- ) ) ) ;
133- } ;
134- Ok ( Ipv6Net :: from ( * net) )
135- } )
136- . collect :: < Result < _ , _ > > ( ) ?;
130+ . copied ( )
131+ . map ( Into :: into)
132+ . collect ( ) ;
137133 PrivateIpConfig :: V6 ( PrivateIpv6Config :: new_with_transit_ips (
138134 * ip,
139135 ipv6_subnet,
140136 transit_ips,
141137 ) ?)
142138 }
143139 ( Some ( ip) , None ) => {
144- // Check that all transit IPs are IPv4.
145140 let transit_ips = self
146- . transit_ips
141+ . transit_ips_v4
147142 . iter ( )
148- . map ( |net| {
149- let IpNetwork :: V4 ( net) = net else {
150- return Err ( Error :: internal_error ( & format ! (
151- "NIC with ID '{}' is IPv4-only, but has \
152- IPv6 transit IPs",
153- self . id( ) ,
154- ) ) ) ;
155- } ;
156- Ok ( Ipv4Net :: from ( * net) )
157- } )
158- . collect :: < Result < _ , _ > > ( ) ?;
143+ . copied ( )
144+ . map ( Into :: into)
145+ . collect ( ) ;
159146 PrivateIpConfig :: V4 ( PrivateIpv4Config :: new_with_transit_ips (
160147 * ip,
161148 ipv4_subnet,
@@ -164,20 +151,16 @@ impl NetworkInterface {
164151 }
165152 ( Some ( ipv4) , Some ( ipv6) ) => {
166153 let ipv4_transit_ips = self
167- . transit_ips
154+ . transit_ips_v4
168155 . iter ( )
169- . filter_map ( |net| match net {
170- IpNetwork :: V4 ( net) => Some ( Ipv4Net :: from ( * net) ) ,
171- IpNetwork :: V6 ( _) => None ,
172- } )
156+ . copied ( )
157+ . map ( Into :: into)
173158 . collect ( ) ;
174159 let ipv6_transit_ips = self
175- . transit_ips
160+ . transit_ips_v6
176161 . iter ( )
177- . filter_map ( |net| match net {
178- IpNetwork :: V6 ( net) => Some ( Ipv6Net :: from ( * net) ) ,
179- IpNetwork :: V4 ( _) => None ,
180- } )
162+ . copied ( )
163+ . map ( Into :: into)
181164 . collect ( ) ;
182165 let v4 = PrivateIpv4Config :: new_with_transit_ips (
183166 * ipv4,
@@ -241,7 +224,8 @@ pub struct InstanceNetworkInterface {
241224 pub slot : SqlU8 ,
242225 #[ diesel( column_name = is_primary) ]
243226 pub primary : bool ,
244- pub transit_ips : Vec < IpNetwork > ,
227+ pub transit_ips_v4 : Vec < crate :: Ipv4Net > ,
228+ pub transit_ips_v6 : Vec < crate :: Ipv6Net > ,
245229}
246230
247231/// Service Network Interface DB model.
@@ -353,7 +337,8 @@ impl NetworkInterface {
353337 ipv6 : self . ipv6 ,
354338 slot : self . slot ,
355339 primary : self . primary ,
356- transit_ips : self . transit_ips ,
340+ transit_ips_v4 : self . transit_ips_v4 ,
341+ transit_ips_v6 : self . transit_ips_v6 ,
357342 }
358343 }
359344
@@ -404,7 +389,8 @@ impl From<InstanceNetworkInterface> for NetworkInterface {
404389 ipv6 : iface. ipv6 ,
405390 slot : iface. slot ,
406391 primary : iface. primary ,
407- transit_ips : iface. transit_ips ,
392+ transit_ips_v4 : iface. transit_ips_v4 ,
393+ transit_ips_v6 : iface. transit_ips_v6 ,
408394 }
409395 }
410396}
@@ -429,7 +415,8 @@ impl From<ServiceNetworkInterface> for NetworkInterface {
429415 ipv6 : iface. ipv6 ,
430416 slot : iface. slot ,
431417 primary : iface. primary ,
432- transit_ips : vec ! [ ] ,
418+ transit_ips_v4 : vec ! [ ] ,
419+ transit_ips_v6 : vec ! [ ] ,
433420 }
434421 }
435422}
@@ -513,15 +500,19 @@ impl IpConfig {
513500 IpConfig :: V4 ( Ipv4Config :: default ( ) )
514501 }
515502
516- /// Return the IPv4 address assignment .
517- pub fn ipv4_assignment ( & self ) -> Option < & Ipv4Assignment > {
503+ /// Return the IPv4 configuration, if it exists .
504+ pub fn ipv4_config ( & self ) -> Option < & Ipv4Config > {
518505 match self {
519- IpConfig :: V4 ( Ipv4Config { ip , .. } ) => Some ( ip ) ,
506+ IpConfig :: V4 ( v4 ) | IpConfig :: DualStack { v4 , .. } => Some ( v4 ) ,
520507 IpConfig :: V6 ( _) => None ,
521- IpConfig :: DualStack { v4 : Ipv4Config { ip, .. } , .. } => Some ( ip) ,
522508 }
523509 }
524510
511+ /// Return the IPv4 address assignment.
512+ pub fn ipv4_assignment ( & self ) -> Option < & Ipv4Assignment > {
513+ self . ipv4_config ( ) . map ( |v4| & v4. ip )
514+ }
515+
525516 /// Return the IPv4 address explicitly requested, if one exists.
526517 pub fn ipv4_addr ( & self ) -> Option < & std:: net:: Ipv4Addr > {
527518 self . ipv4_assignment ( ) . and_then ( |assignment| match assignment {
@@ -530,6 +521,11 @@ impl IpConfig {
530521 } )
531522 }
532523
524+ /// Return the IPv4 transit IPs, if any.
525+ pub fn ipv4_transit_ips ( & self ) -> Option < Vec < Ipv4Net > > {
526+ self . ipv4_config ( ) . map ( |v4| v4. transit_ips . clone ( ) )
527+ }
528+
533529 /// Construct an IPv6 configuration with no transit IPs.
534530 pub fn from_ipv6 ( addr : std:: net:: Ipv6Addr ) -> Self {
535531 IpConfig :: V6 ( Ipv6Config {
@@ -543,15 +539,19 @@ impl IpConfig {
543539 IpConfig :: V6 ( Ipv6Config :: default ( ) )
544540 }
545541
546- /// Return the IPv6 address assignment .
547- pub fn ipv6_assignment ( & self ) -> Option < & Ipv6Assignment > {
542+ /// Return the IPv6 configuration, if it exists .
543+ pub fn ipv6_config ( & self ) -> Option < & Ipv6Config > {
548544 match self {
549- IpConfig :: V6 ( Ipv6Config { ip , .. } ) => Some ( ip ) ,
545+ IpConfig :: V6 ( v6 ) | IpConfig :: DualStack { v6 , .. } => Some ( v6 ) ,
550546 IpConfig :: V4 ( _) => None ,
551- IpConfig :: DualStack { v6 : Ipv6Config { ip, .. } , .. } => Some ( ip) ,
552547 }
553548 }
554549
550+ /// Return the IPv6 address assignment.
551+ pub fn ipv6_assignment ( & self ) -> Option < & Ipv6Assignment > {
552+ self . ipv6_config ( ) . map ( |v6| & v6. ip )
553+ }
554+
555555 /// Return the IPv6 address explicitly requested, if one exists.
556556 pub fn ipv6_addr ( & self ) -> Option < & std:: net:: Ipv6Addr > {
557557 self . ipv6_assignment ( ) . and_then ( |assignment| match assignment {
@@ -560,25 +560,24 @@ impl IpConfig {
560560 } )
561561 }
562562
563+ /// Return the IPv6 transit IPs, if any.
564+ pub fn ipv6_transit_ips ( & self ) -> Option < Vec < Ipv6Net > > {
565+ self . ipv6_config ( ) . map ( |v6| v6. transit_ips . clone ( ) )
566+ }
567+
563568 /// Return the transit IPs requested in this configuration.
564569 pub fn transit_ips ( & self ) -> Vec < IpNet > {
565- match self {
566- IpConfig :: V4 ( Ipv4Config { transit_ips, .. } ) => {
567- transit_ips. iter ( ) . copied ( ) . map ( Into :: into) . collect ( )
568- }
569- IpConfig :: V6 ( Ipv6Config { transit_ips, .. } ) => {
570- transit_ips. iter ( ) . copied ( ) . map ( Into :: into) . collect ( )
571- }
572- IpConfig :: DualStack {
573- v4 : Ipv4Config { transit_ips : ipv4_addrs, .. } ,
574- v6 : Ipv6Config { transit_ips : ipv6_addrs, .. } ,
575- } => ipv4_addrs
576- . iter ( )
577- . copied ( )
578- . map ( Into :: into)
579- . chain ( ipv6_addrs. iter ( ) . copied ( ) . map ( Into :: into) )
580- . collect ( ) ,
581- }
570+ self . ipv4_transit_ips ( )
571+ . unwrap_or_default ( )
572+ . into_iter ( )
573+ . map ( Into :: into)
574+ . chain (
575+ self . ipv6_transit_ips ( )
576+ . unwrap_or_default ( )
577+ . into_iter ( )
578+ . map ( Into :: into) ,
579+ )
580+ . collect ( )
582581 }
583582
584583 /// Construct a dual-stack IP configuration with explicit IP addresses.
@@ -775,7 +774,9 @@ pub struct NetworkInterfaceUpdate {
775774 pub time_modified : DateTime < Utc > ,
776775 #[ diesel( column_name = is_primary) ]
777776 pub primary : Option < bool > ,
778- pub transit_ips : Vec < IpNetwork > ,
777+ #[ diesel( column_name = transit_ips) ]
778+ pub transit_ips_v4 : Vec < crate :: Ipv4Net > ,
779+ pub transit_ips_v6 : Vec < crate :: Ipv6Net > ,
779780}
780781
781782impl From < InstanceNetworkInterface > for external:: InstanceNetworkInterface {
@@ -791,10 +792,13 @@ impl From<InstanceNetworkInterface> for external::InstanceNetworkInterface {
791792 ip,
792793 mac : * iface. mac ,
793794 primary : iface. primary ,
795+ // https://github.com/oxidecomputer/omicron/issues/9248 Separate
796+ // these into IP versions.
794797 transit_ips : iface
795- . transit_ips
798+ . transit_ips_v4
796799 . into_iter ( )
797- . map ( Into :: into)
800+ . map ( |v4| v4. 0 . into ( ) )
801+ . chain ( iface. transit_ips_v6 . into_iter ( ) . map ( |v6| v6. 0 . into ( ) ) )
798802 . collect ( ) ,
799803 }
800804 }
@@ -803,16 +807,18 @@ impl From<InstanceNetworkInterface> for external::InstanceNetworkInterface {
803807impl From < params:: InstanceNetworkInterfaceUpdate > for NetworkInterfaceUpdate {
804808 fn from ( params : params:: InstanceNetworkInterfaceUpdate ) -> Self {
805809 let primary = if params. primary { Some ( true ) } else { None } ;
810+ let ( transit_ips_v4, transit_ips_v6) : ( Vec < _ > , Vec < _ > ) =
811+ params. transit_ips . into_iter ( ) . partition_map ( |net| match net {
812+ IpNet :: V4 ( v4) => Either :: Left ( crate :: Ipv4Net :: from ( v4) ) ,
813+ IpNet :: V6 ( v6) => Either :: Right ( crate :: Ipv6Net :: from ( v6) ) ,
814+ } ) ;
806815 Self {
807816 name : params. identity . name . map ( |n| n. into ( ) ) ,
808817 description : params. identity . description ,
809818 time_modified : Utc :: now ( ) ,
810819 primary,
811- transit_ips : params
812- . transit_ips
813- . into_iter ( )
814- . map ( Into :: into)
815- . collect ( ) ,
820+ transit_ips_v4,
821+ transit_ips_v6,
816822 }
817823 }
818824}
0 commit comments