route_rule: support from all to all
routing policy
Add support to `from all to all` routing policy. In static config generator it will add `from 0.0.0.0/0` or `from ::/0` to the keyfile. A new parameter `family` have been introduced to the route-rule config field. This parameter is used to specify the address family. If `family` is not specified it will fail on validation, if not specified from Nispor it will assume IPv4 as it was done in the past. Example: ```yaml route-rules: config: - route-table: 254 priority: 100 family: ipv4 ``` Integration test cases added. Fixes #1417 Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
This commit is contained in:
parent
84e0b3e176
commit
c53dc56824
@ -73,8 +73,8 @@ pub struct BaseInterface {
|
||||
pub ipv6: Option<InterfaceIpv6>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Interface wide MPTCP flags.
|
||||
/// Nmstate will apply these flags to all valid IP addresses(both static and
|
||||
/// dynamic).
|
||||
/// Nmstate will apply these flags to all valid IP addresses(both static
|
||||
/// and dynamic).
|
||||
pub mptcp: Option<MptcpConfig>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
// None here mean no change, empty string mean detach from controller.
|
||||
|
@ -964,7 +964,7 @@ fn default_allow_extra_address() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[non_exhaustive]
|
||||
pub enum AddressFamily {
|
||||
|
@ -1,21 +1,69 @@
|
||||
use crate::{RouteRuleEntry, RouteRules};
|
||||
use log::warn;
|
||||
|
||||
pub(crate) fn get_route_rules(np_rules: &[nispor::RouteRule]) -> RouteRules {
|
||||
use crate::{AddressFamily, RouteRuleEntry, RouteRules};
|
||||
|
||||
// Due to a bug in NetworkManager all route rules added using NetworkManager are
|
||||
// using RTM_PROTOCOL UnSpec. Therefore, we need to support it until it is
|
||||
// fixed.
|
||||
const SUPPORTED_STATIC_ROUTE_PROTOCOL: [nispor::RouteProtocol; 3] = [
|
||||
nispor::RouteProtocol::Boot,
|
||||
nispor::RouteProtocol::Static,
|
||||
nispor::RouteProtocol::UnSpec,
|
||||
];
|
||||
|
||||
const SUPPORTED_ROUTE_PROTOCOL: [nispor::RouteProtocol; 8] = [
|
||||
nispor::RouteProtocol::Boot,
|
||||
nispor::RouteProtocol::Static,
|
||||
nispor::RouteProtocol::Ra,
|
||||
nispor::RouteProtocol::Dhcp,
|
||||
nispor::RouteProtocol::Mrouted,
|
||||
nispor::RouteProtocol::KeepAlived,
|
||||
nispor::RouteProtocol::Babel,
|
||||
nispor::RouteProtocol::UnSpec,
|
||||
];
|
||||
|
||||
pub(crate) fn get_route_rules(
|
||||
np_rules: &[nispor::RouteRule],
|
||||
running_config_only: bool,
|
||||
) -> RouteRules {
|
||||
let mut ret = RouteRules::new();
|
||||
|
||||
let mut rules = Vec::new();
|
||||
let protocols = if running_config_only {
|
||||
SUPPORTED_STATIC_ROUTE_PROTOCOL.as_slice()
|
||||
} else {
|
||||
SUPPORTED_ROUTE_PROTOCOL.as_slice()
|
||||
};
|
||||
|
||||
for np_rule in np_rules {
|
||||
let mut rule = RouteRuleEntry::new();
|
||||
// We only support route rules with 'table' action
|
||||
if np_rule.action != nispor::RuleAction::Table {
|
||||
continue;
|
||||
}
|
||||
// Filter out the routes with protocols that we do not support
|
||||
if let Some(rule_protocol) = np_rule.protocol.as_ref() {
|
||||
if !protocols.contains(rule_protocol) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
rule.ip_to = np_rule.dst.clone();
|
||||
rule.ip_from = np_rule.src.clone();
|
||||
rule.table_id = np_rule.table;
|
||||
rule.priority = np_rule.priority.map(i64::from);
|
||||
rule.fwmark = np_rule.fw_mark;
|
||||
rule.fwmask = np_rule.fw_mask;
|
||||
rule.family = match np_rule.address_family {
|
||||
nispor::AddressFamily::IPv4 => Some(AddressFamily::IPv4),
|
||||
nispor::AddressFamily::IPv6 => Some(AddressFamily::IPv6),
|
||||
_ => {
|
||||
warn!(
|
||||
"Unsupported route rule family {:?}",
|
||||
np_rule.address_family
|
||||
);
|
||||
None
|
||||
}
|
||||
};
|
||||
rules.push(rule);
|
||||
}
|
||||
ret.config = Some(rules);
|
||||
|
@ -123,7 +123,7 @@ pub(crate) fn nispor_retrieve(
|
||||
}
|
||||
set_controller_type(&mut net_state.interfaces);
|
||||
net_state.routes = get_routes(running_config_only);
|
||||
net_state.rules = get_route_rules(&np_state.rules);
|
||||
net_state.rules = get_route_rules(&np_state.rules, running_config_only);
|
||||
|
||||
Ok(net_state)
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use log::warn;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::super::NmIpRouteRule;
|
||||
|
||||
const DEFAULT_ROUTE_TABLE: u32 = 254;
|
||||
const AF_INET: i32 = 2;
|
||||
|
||||
impl NmIpRouteRule {
|
||||
pub(crate) fn to_keyfile(&self) -> HashMap<String, String> {
|
||||
@ -23,10 +25,25 @@ impl NmIpRouteRule {
|
||||
keys.push(to_str);
|
||||
}
|
||||
|
||||
let from_str = match (self.from.as_ref(), self.from_len.as_ref()) {
|
||||
(Some(f), Some(f_len)) => format!("from {f}/{f_len}"),
|
||||
(Some(f), None) => format!("from {f}"),
|
||||
_ => "".to_string(),
|
||||
let mut from_str =
|
||||
match (self.from.as_ref(), self.from_len.as_ref()) {
|
||||
(Some(f), Some(f_len)) => format!("from {f}/{f_len}"),
|
||||
(Some(f), None) => format!("from {f}"),
|
||||
_ => "".to_string(),
|
||||
};
|
||||
from_str = if self.from.is_none() && self.to.is_none() {
|
||||
if let Some(family) = self.family {
|
||||
if family == AF_INET {
|
||||
"from 0.0.0.0/0".to_string()
|
||||
} else {
|
||||
"from ::/0".to_string()
|
||||
}
|
||||
} else {
|
||||
warn!("Neither from, to or family specified on route rule. Assuming IPv4.");
|
||||
"from 0.0.0.0/0".to_string()
|
||||
}
|
||||
} else {
|
||||
from_str
|
||||
};
|
||||
if !from_str.is_empty() {
|
||||
keys.push(from_str);
|
||||
|
@ -6,18 +6,18 @@ use super::{
|
||||
};
|
||||
use crate::nm::nm_dbus::{NmConnection, NmSettingIp, NmSettingIpMethod};
|
||||
use crate::{
|
||||
BaseInterface, Dhcpv4ClientId, Dhcpv6Duid, ErrorKind, Interface,
|
||||
InterfaceIpv4, InterfaceIpv6, Ipv6AddrGenMode, NmstateError, RouteEntry,
|
||||
RouteRuleEntry, WaitIp,
|
||||
AddressFamily, BaseInterface, Dhcpv4ClientId, Dhcpv6Duid, ErrorKind,
|
||||
Interface, InterfaceIpv4, InterfaceIpv6, Ipv6AddrGenMode, NmstateError,
|
||||
RouteEntry, RouteRuleEntry, WaitIp,
|
||||
};
|
||||
|
||||
const ADDR_GEN_MODE_EUI64: i32 = 0;
|
||||
const ADDR_GEN_MODE_STABLE_PRIVACY: i32 = 1;
|
||||
|
||||
fn gen_nm_ipv4_setting(
|
||||
fn gen_nm_ipv4_setting<'a>(
|
||||
iface_ip: Option<&InterfaceIpv4>,
|
||||
routes: Option<&[RouteEntry]>,
|
||||
rules: Option<&[RouteRuleEntry]>,
|
||||
rules: impl std::iter::Iterator<Item = &'a RouteRuleEntry>,
|
||||
nm_conn: &mut NmConnection,
|
||||
) -> Result<(), NmstateError> {
|
||||
let iface_ip = match iface_ip {
|
||||
@ -85,9 +85,7 @@ fn gen_nm_ipv4_setting(
|
||||
// Clean up static routes if ip is disabled
|
||||
nm_setting.routes = Vec::new();
|
||||
}
|
||||
if let Some(rules) = rules {
|
||||
nm_setting.route_rules = gen_nm_ip_rules(rules, false)?;
|
||||
}
|
||||
nm_setting.route_rules = gen_nm_ip_rules(rules, false)?;
|
||||
if let Some(dns) = &iface_ip.dns {
|
||||
apply_nm_dns_setting(&mut nm_setting, dns);
|
||||
}
|
||||
@ -95,10 +93,10 @@ fn gen_nm_ipv4_setting(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn gen_nm_ipv6_setting(
|
||||
fn gen_nm_ipv6_setting<'a>(
|
||||
iface_ip: Option<&InterfaceIpv6>,
|
||||
routes: Option<&[RouteEntry]>,
|
||||
rules: Option<&[RouteRuleEntry]>,
|
||||
rules: impl std::iter::Iterator<Item = &'a RouteRuleEntry>,
|
||||
nm_conn: &mut NmConnection,
|
||||
) -> Result<(), NmstateError> {
|
||||
let iface_ip = match iface_ip {
|
||||
@ -179,9 +177,7 @@ fn gen_nm_ipv6_setting(
|
||||
nm_setting.routes = gen_nm_ip_routes(routes, true)?;
|
||||
}
|
||||
}
|
||||
if let Some(rules) = rules {
|
||||
nm_setting.route_rules = gen_nm_ip_rules(rules, true)?;
|
||||
}
|
||||
nm_setting.route_rules = gen_nm_ip_rules(rules, true)?;
|
||||
if let Some(dns) = &iface_ip.dns {
|
||||
apply_nm_dns_setting(&mut nm_setting, dns);
|
||||
}
|
||||
@ -197,8 +193,24 @@ pub(crate) fn gen_nm_ip_setting(
|
||||
) -> Result<(), NmstateError> {
|
||||
let base_iface = iface.base_iface();
|
||||
if base_iface.can_have_ip() {
|
||||
gen_nm_ipv4_setting(base_iface.ipv4.as_ref(), routes, rules, nm_conn)?;
|
||||
gen_nm_ipv6_setting(base_iface.ipv6.as_ref(), routes, rules, nm_conn)?;
|
||||
gen_nm_ipv4_setting(
|
||||
base_iface.ipv4.as_ref(),
|
||||
routes,
|
||||
rules
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.filter(|r| r.family == Some(AddressFamily::IPv4)),
|
||||
nm_conn,
|
||||
)?;
|
||||
gen_nm_ipv6_setting(
|
||||
base_iface.ipv6.as_ref(),
|
||||
routes,
|
||||
rules
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.filter(|r| r.family == Some(AddressFamily::IPv6)),
|
||||
nm_conn,
|
||||
)?;
|
||||
apply_nmstate_wait_ip(base_iface, nm_conn);
|
||||
} else {
|
||||
nm_conn.ipv4 = None;
|
||||
|
@ -4,7 +4,10 @@ use std::convert::TryFrom;
|
||||
|
||||
use super::super::nm_dbus::NmIpRouteRule;
|
||||
|
||||
use crate::{ip::is_ipv6_addr, InterfaceIpAddr, NmstateError, RouteRuleEntry};
|
||||
use crate::{
|
||||
ip::is_ipv6_addr, ip::AddressFamily, InterfaceIpAddr, NmstateError,
|
||||
RouteRuleEntry,
|
||||
};
|
||||
|
||||
// NM require route rule priority been set explicitly, use 30,000 when
|
||||
// desire state instruct to use USE_DEFAULT_PRIORITY
|
||||
@ -13,14 +16,19 @@ const ROUTE_RULE_DEFAULT_PRIORIRY: u32 = 30000;
|
||||
const AF_INET6: i32 = 10;
|
||||
const AF_INET: i32 = 2;
|
||||
|
||||
pub(crate) fn gen_nm_ip_rules(
|
||||
rules: &[RouteRuleEntry],
|
||||
pub(crate) fn gen_nm_ip_rules<'a>(
|
||||
rules: impl std::iter::Iterator<Item = &'a RouteRuleEntry>,
|
||||
is_ipv6: bool,
|
||||
) -> Result<Vec<NmIpRouteRule>, NmstateError> {
|
||||
let mut ret = Vec::new();
|
||||
for rule in rules {
|
||||
let mut nm_rule = NmIpRouteRule::default();
|
||||
nm_rule.family = Some(if is_ipv6 { AF_INET6 } else { AF_INET });
|
||||
if let Some(family) = rule.family {
|
||||
if is_ipv6 != matches!(family, AddressFamily::IPv6) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if let Some(addr) = rule.ip_from.as_deref() {
|
||||
match (is_ipv6, is_ipv6_addr(addr)) {
|
||||
(true, true) | (false, false) => {
|
||||
|
@ -4,7 +4,7 @@ use std::convert::TryFrom;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
ip::{is_ipv6_addr, sanitize_ip_network},
|
||||
ip::{is_ipv6_addr, sanitize_ip_network, AddressFamily},
|
||||
ErrorKind, InterfaceIpAddr, NmstateError,
|
||||
};
|
||||
|
||||
@ -222,8 +222,11 @@ impl Default for RouteRuleState {
|
||||
#[non_exhaustive]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RouteRuleEntry {
|
||||
/// Indicate the address family of the route rule.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub family: Option<AddressFamily>,
|
||||
/// Indicate this is normal route rule or absent route rule.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub state: Option<RouteRuleState>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Source prefix to match.
|
||||
@ -280,20 +283,50 @@ impl RouteRuleEntry {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
// * Neither ip_from nor ip_to should be defined
|
||||
pub(crate) fn validate(&self) -> Result<(), NmstateError> {
|
||||
if self.ip_from.is_none() && self.ip_to.is_none() {
|
||||
if self.ip_from.is_none()
|
||||
&& self.ip_to.is_none()
|
||||
&& self.family.is_none()
|
||||
{
|
||||
let e = NmstateError::new(
|
||||
ErrorKind::InvalidArgument,
|
||||
format!(
|
||||
"Neither ip-from or ip-to is defined in route rule {:?}",
|
||||
"Neither ip-from, ip-to nor family is defined {:?}",
|
||||
self
|
||||
),
|
||||
);
|
||||
log::error!("{}", e);
|
||||
return Err(e);
|
||||
} else if let Some(family) = self.family {
|
||||
if let Some(ip_from) = self.ip_from.as_ref() {
|
||||
if is_ipv6_addr(ip_from.as_str())
|
||||
!= matches!(family, AddressFamily::IPv6)
|
||||
{
|
||||
let e = NmstateError::new(
|
||||
ErrorKind::InvalidArgument,
|
||||
format!("The ip-from format mismatches with the family set {:?}",
|
||||
self
|
||||
),
|
||||
);
|
||||
log::error!("{}", e);
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
if let Some(ip_to) = self.ip_to.as_ref() {
|
||||
if is_ipv6_addr(ip_to.as_str())
|
||||
!= matches!(family, AddressFamily::IPv6)
|
||||
{
|
||||
let e = NmstateError::new(
|
||||
ErrorKind::InvalidArgument,
|
||||
format!("The ip-to format mismatches with the family set {:?}",
|
||||
self
|
||||
),
|
||||
);
|
||||
log::error!("{}", e);
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.fwmark.is_none() && self.fwmask.is_some() {
|
||||
let e = NmstateError::new(
|
||||
ErrorKind::InvalidArgument,
|
||||
@ -375,9 +408,11 @@ impl RouteRuleEntry {
|
||||
!is_ipv6_addr(ip_from.as_str())
|
||||
} else if let Some(ip_to) = self.ip_to.as_ref() {
|
||||
!is_ipv6_addr(ip_to.as_str())
|
||||
} else if let Some(family) = self.family.as_ref() {
|
||||
*family == AddressFamily::IPv4
|
||||
} else {
|
||||
log::warn!(
|
||||
"Neither ip-from nor ip-to \
|
||||
"Neither ip-from, ip-to nor family \
|
||||
is defined, treating it a IPv4 route rule"
|
||||
);
|
||||
true
|
||||
@ -397,6 +432,12 @@ impl RouteRuleEntry {
|
||||
pub(crate) fn sanitize(&mut self) -> Result<(), NmstateError> {
|
||||
if let Some(ip) = self.ip_from.as_ref() {
|
||||
let new_ip = sanitize_ip_network(ip)?;
|
||||
if self.family.is_none() {
|
||||
match is_ipv6_addr(new_ip.as_str()) {
|
||||
true => self.family = Some(AddressFamily::IPv6),
|
||||
false => self.family = Some(AddressFamily::IPv4),
|
||||
};
|
||||
}
|
||||
if ip != &new_ip {
|
||||
log::warn!("Route rule ip-from {} sanitized to {}", ip, new_ip);
|
||||
self.ip_from = Some(new_ip);
|
||||
@ -404,6 +445,12 @@ impl RouteRuleEntry {
|
||||
}
|
||||
if let Some(ip) = self.ip_to.as_ref() {
|
||||
let new_ip = sanitize_ip_network(ip)?;
|
||||
if self.family.is_none() {
|
||||
match is_ipv6_addr(new_ip.as_str()) {
|
||||
true => self.family = Some(AddressFamily::IPv6),
|
||||
false => self.family = Some(AddressFamily::IPv4),
|
||||
};
|
||||
}
|
||||
if ip != &new_ip {
|
||||
log::warn!("Route rule ip-to {} sanitized to {}", ip, new_ip);
|
||||
self.ip_to = Some(new_ip);
|
||||
|
@ -159,6 +159,7 @@ fn gen_rule_entry(
|
||||
fwmask: u32,
|
||||
) -> RouteRuleEntry {
|
||||
RouteRuleEntry {
|
||||
family: None,
|
||||
state: None,
|
||||
ip_from: Some(ip_from.to_string()),
|
||||
ip_to: Some(ip_to.to_string()),
|
||||
@ -391,3 +392,33 @@ ip-from: 2001:db8:2:0000::ffff
|
||||
assert_eq!(rule.ip_to.unwrap(), "2001:db8:1::2/128");
|
||||
assert_eq!(rule.ip_from.unwrap(), "2001:db8:2::ffff/128");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_route_rule_validate_ipv6_family_ip_from() {
|
||||
let rule: RouteRuleEntry = serde_yaml::from_str(
|
||||
r#"
|
||||
ip-from: 2001:db8:b::/64
|
||||
priority: 30000
|
||||
route-table: 200
|
||||
family: ipv6
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
rule.validate().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_route_rule_validate_ipv4_family_ip_from() {
|
||||
let rule: RouteRuleEntry = serde_yaml::from_str(
|
||||
r#"
|
||||
ip-from: 192.168.2.0/24
|
||||
priority: 30000
|
||||
route-table: 200
|
||||
family: ipv4
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
rule.validate().unwrap();
|
||||
}
|
||||
|
@ -68,6 +68,9 @@ class RouteRule:
|
||||
STATE_ABSENT = "absent"
|
||||
FWMARK = "fwmark"
|
||||
FWMASK = "fwmask"
|
||||
FAMILY = "family"
|
||||
FAMILY_IPV4 = "ipv4"
|
||||
FAMILY_IPV6 = "ipv6"
|
||||
|
||||
|
||||
class DNS:
|
||||
|
@ -160,6 +160,7 @@ def test_add_remove_route_rule(eth1_up):
|
||||
rule.get(RouteRule.ROUTE_TABLE),
|
||||
rule.get(RouteRule.FWMARK),
|
||||
rule.get(RouteRule.FWMASK),
|
||||
rule.get(RouteRule.FAMILY),
|
||||
)
|
||||
|
||||
|
||||
|
@ -118,6 +118,7 @@ route-rules:
|
||||
rule.get(RouteRule.ROUTE_TABLE),
|
||||
rule.get(RouteRule.FWMARK),
|
||||
rule.get(RouteRule.FWMASK),
|
||||
rule.get(RouteRule.FAMILY),
|
||||
)
|
||||
|
||||
|
||||
|
@ -310,9 +310,11 @@ def eth1_with_static_route_and_rule(eth1_up):
|
||||
- ip-from: 2001:db8:b::/64
|
||||
priority: 30000
|
||||
route-table: 200
|
||||
family: ipv6
|
||||
- ip-from: 192.168.3.2/32
|
||||
priority: 30001
|
||||
route-table: 200
|
||||
family: ipv4
|
||||
interfaces:
|
||||
- name: eth1
|
||||
type: ethernet
|
||||
|
@ -939,6 +939,7 @@ def test_add_route_rule_to_ovs_interface_dhcp_auto_route_table(
|
||||
route_rule.get(RouteRule.ROUTE_TABLE),
|
||||
route_rule.get(RouteRule.FWMARK),
|
||||
route_rule.get(RouteRule.FWMASK),
|
||||
route_rule.get(RouteRule.FAMILY),
|
||||
)
|
||||
|
||||
|
||||
|
@ -670,7 +670,7 @@ def route_rule_test_env(eth1_static_gateway_dns):
|
||||
|
||||
|
||||
@pytest.mark.tier1
|
||||
def test_route_rule_add_without_from_or_to(route_rule_test_env):
|
||||
def test_route_rule_add_without_from_or_to_or_family(route_rule_test_env):
|
||||
state = route_rule_test_env
|
||||
state[RouteRule.KEY] = {
|
||||
RouteRule.CONFIG: [
|
||||
@ -1002,6 +1002,67 @@ def test_route_rule_fwmark_with_fwmask(route_rule_test_env):
|
||||
_check_ip_rules(rules)
|
||||
|
||||
|
||||
@pytest.mark.tier1
|
||||
def test_route_rule_from_all_to_all(route_rule_test_env):
|
||||
state = route_rule_test_env
|
||||
rules = [
|
||||
{
|
||||
RouteRule.ROUTE_TABLE: IPV6_ROUTE_TABLE_ID1,
|
||||
RouteRule.PRIORITY: 100,
|
||||
RouteRule.FAMILY: RouteRule.FAMILY_IPV6,
|
||||
},
|
||||
{
|
||||
RouteRule.ROUTE_TABLE: IPV4_ROUTE_TABLE_ID1,
|
||||
RouteRule.PRIORITY: 100,
|
||||
RouteRule.FAMILY: RouteRule.FAMILY_IPV4,
|
||||
},
|
||||
]
|
||||
state[RouteRule.KEY] = {RouteRule.CONFIG: rules}
|
||||
|
||||
libnmstate.apply(state)
|
||||
_check_ip_rules(rules)
|
||||
|
||||
|
||||
@pytest.mark.tier1
|
||||
def test_route_rule_from_all_to_all_ipv4(route_rule_test_env):
|
||||
state = route_rule_test_env
|
||||
rules = [
|
||||
{
|
||||
RouteRule.ROUTE_TABLE: IPV4_ROUTE_TABLE_ID1,
|
||||
RouteRule.PRIORITY: 100,
|
||||
RouteRule.FAMILY: RouteRule.FAMILY_IPV4,
|
||||
},
|
||||
]
|
||||
state[RouteRule.KEY] = {RouteRule.CONFIG: rules}
|
||||
|
||||
libnmstate.apply(state)
|
||||
_check_ip_rules(rules)
|
||||
|
||||
rules[0][RouteRule.FAMILY] = RouteRule.FAMILY_IPV6
|
||||
with pytest.raises(AssertionError):
|
||||
assert _check_ip_rules(rules)
|
||||
|
||||
|
||||
@pytest.mark.tier1
|
||||
def test_route_rule_from_all_to_all_ipv6(route_rule_test_env):
|
||||
state = route_rule_test_env
|
||||
rules = [
|
||||
{
|
||||
RouteRule.ROUTE_TABLE: IPV6_ROUTE_TABLE_ID1,
|
||||
RouteRule.PRIORITY: 100,
|
||||
RouteRule.FAMILY: RouteRule.FAMILY_IPV6,
|
||||
},
|
||||
]
|
||||
state[RouteRule.KEY] = {RouteRule.CONFIG: rules}
|
||||
|
||||
libnmstate.apply(state)
|
||||
_check_ip_rules(rules)
|
||||
|
||||
rules[0][RouteRule.FAMILY] = RouteRule.FAMILY_IPV4
|
||||
with pytest.raises(AssertionError):
|
||||
assert _check_ip_rules(rules)
|
||||
|
||||
|
||||
def _check_ip_rules(rules):
|
||||
for rule in rules:
|
||||
iprule.ip_rule_exist_in_os(
|
||||
@ -1011,6 +1072,7 @@ def _check_ip_rules(rules):
|
||||
rule.get(RouteRule.ROUTE_TABLE),
|
||||
rule.get(RouteRule.FWMARK),
|
||||
rule.get(RouteRule.FWMASK),
|
||||
rule.get(RouteRule.FAMILY),
|
||||
)
|
||||
|
||||
|
||||
|
@ -24,12 +24,16 @@ from libnmstate import iplib
|
||||
from . import cmdlib
|
||||
|
||||
|
||||
def ip_rule_exist_in_os(ip_from, ip_to, priority, table, fwmark, fwmask):
|
||||
def ip_rule_exist_in_os(
|
||||
ip_from, ip_to, priority, table, fwmark, fwmask, family
|
||||
):
|
||||
expected_rule = locals()
|
||||
logging.debug("Checking ip rule for {}".format(expected_rule))
|
||||
cmds = ["ip"]
|
||||
if (ip_from and iplib.is_ipv6_address(ip_from)) or (
|
||||
ip_to and iplib.is_ipv6_address(ip_to)
|
||||
if (
|
||||
(ip_from and iplib.is_ipv6_address(ip_from))
|
||||
or (ip_to and iplib.is_ipv6_address(ip_to))
|
||||
or (family and family == "ipv6")
|
||||
):
|
||||
cmds.append("-6")
|
||||
if ip_from and "/" not in ip_from:
|
||||
|
Loading…
x
Reference in New Issue
Block a user