On 01/04/2021 13:28, gmingchen(陈供明) wrote:
> Correct some words
> 
> 
> On 2021/4/1, 8:17 PM, "gmingchen(陈供明)" <[email protected]> wrote:
> 
> 
> 
>     On 2021/3/30, 4:59 PM, "Mark Gray" <[email protected]> wrote:
> 
>         On 29/03/2021 13:30, gmingchen(陈供明) wrote:
>         > 
>         > On 2021/3/25, 11:30 PM, "Mark Gray" <[email protected]> wrote:
>         > 
>         >     On 19/03/2021 13:01, Dumitru Ceara wrote:
>         >     > On 3/10/21 2:29 PM, gmingchen(陈供明) wrote:
>         >     >> From: Gongming Chen <[email protected]>
>         >     >>
>         > 
>         >     Thanks for the patch. Looks like a lot of thought went into the
>         >     algorithm and it has been interesting to review.
>         > 
>         >     Do you know if there are any well-known algorithms to do this? 
> It seems
>         >     like a common problem? If there was, it may be better to use it 
> as we
>         >     could reference standard documentation.
>         > 
>         > Hi Mark,
>         > First of all, thanks for the review.
>         > 
>         > Unfortunately, I did not find a well-known algorithms.
> 
>         Ok. Thats a shame. It's kind of like IP subnetting so I thought there
>         may be something.
> 
>         > 
>         >     >> This patch merges ipv4 addresses with wildcard masks, and 
> replaces this
>         >     >> ipv4 addresses with the combined ip/mask. This will greatly 
> reduce the
>         >     >> entries in the ovs security group flow table, especially 
> when the host
>         >     >> size is large.
>         >     >>
>         >     >> Analysis in the simplest scenario, a network 1.1.1.0/24 
> network,create 253
>         >     >> ports(1.1.1.2-1.1.1.254).
>         >     >> Only focus on the number of ip addresses, the original 253 
> addresses will
>         >     >> be combined into 13 addresses.
>         >     >> 1.1.1.2/31
>         >     >> 1.1.1.4/30
>         >     >> 1.1.1.8/29
>         >     >> 1.1.1.16/28
>         >     >> 1.1.1.32/27
>         >     >> 1.1.1.64/26
>         >     >> 1.1.1.128/26
>         >     >> 1.1.1.192/27
>         >     >> 1.1.1.224/28
>         >     >> 1.1.1.240/29
>         >     >> 1.1.1.248/30
>         >     >> 1.1.1.252/31
>         >     >> 1.1.1.254
>         >     >>
>         >     >> Some scenes are similar to the following:
>         >     >>     1.1.1.2, 1.1.1.6
>         >     >> After the combine:
>         >     >>     1.1.1.2/255.255.255.251
>         >     >> You can use ovn-match-ip utility to match ip.
>         >     >> such as:
>         >     >>     ovs-ofctl dump-flows br-int | ovn-match-ip 1.1.1.6
>         >     >>     1.1.1.2/255.255.255.251 will show.
>         >     >>
>         >     >> Simple description of the algorithm.
>         >     >> There are two situations
>         >     >> 1. Combine once
>         >     >> such as:
>         >     >>     1.1.1.0 1.1.1.1 1.0.1.0 1.0.1.1
>         >     >>     Combined into: 1.1.1.0/31, 1.0.1.0/31
>         >     >> 2. Combine multiple times
>         >     >>     1.1.1.0 1.1.1.1 1.0.1.0 1.0.1.1
>         >     >>     Combined into: 1.0.1.0/255.254.255.254
>         >     >>
>         >     >> Considering the actual scene and simplicity, the first case 
> is used to
>         >     >> combine once.
>         >     >>
>         >     >> ...00...
>         >     >> ...01...
>         >     >> ...10...
>         >     >> ...11...
>         >     >> "..." means the same value omitted.
>         >     >> Obviously, the above value can be expressed as 
> ...00.../11100111. This
>         >     >> continuous interval that can be represented by one or 
> several wildcard
>         >     >> masks is called a segment.
>         >     >> Only if all 2<<n values from the minimum value 00...(n) to 
> 11...(n)
>         >     >> exist, can they be combined into 00...(n)/00...( n)
>         >     >>
>         >     >> First sort all the values by size. Iterate through each 
> value.
>         >     >> 1. Find a new segment, where two values differ only by 1 
> bit, such as
>         >     >> ...0... and ...1...
>         >     >>     diff = ip_next ^ ip
>         >     >>     if (diff & (diff-1)) == 0
>         >     >>         new_segment = true
>         >     >> The first non-zero place in the high direction of ip is the 
> end of the
>         >     >> segment(segment_end).
>         >     >> For example...100... and...101..., the segment_end is 
> ...111...
>         >     >>
>         >     >> 2. Count the number of consecutive and less than 
> continuous_size in the
>         >     >> segment.
>         >     >>     diff = ip_next - ip
>         >     >>     if (diff & (diff-1)) == 0 && ip_next <= segment_end
>         >     >>         continuous_size++
>         >     >>
>         >     >> 3. Combine different ip intervals in the segment according to
>         >     >> continuous_size.
>         >     >> In continuous_size, from the highest bit of 1 to the lowest 
> bit of 1, in
>         >     >> the order of segment start, each bit that is 1 is a 
> different ip interval
>         >     >> that can be combined with a wildcard mask.
>         >     >> For example, 000, 001, 010:
>         >     >>     continuous_size: 3 (binary 11), segment_start: 000
>         >     >>     mask: ~(1 << 1 - 1) = 110; ~(1 << 0 - 1) = 111;
>         >     >>     Combined to: 000/110, 010/111
>         >     >>
>         >     >> 4. The ip that cannot be recorded in a segment will not be 
> combined.
>         >     >>
>         >     >> Signed-off-by: Gongming Chen <[email protected]>
>         >     >> ---
>         >     > 
>         >     > Hi Gongming,
>         >     > 
>         >     > Sorry for the delayed review.
>         >     > 
>         >     > I have a few general remarks/concerns and some specific 
> comments inline.
>         >     >  First, the general remarks.
>         >     > 
>         >     > I'm wondering if it would make more sense for this wildcard 
> combination
>         >     > of IPs to be done outside OVN, in the CMS, for the following 
> reasons:
>         >     > 
>         >     > - it's IPv4 specific.
>         >     > - the CMS usually has more information about when it is 
> matching on IP
>         >     > sets and can probably optimize the match it uses in the ACL.
>         > 
>         > 
>         >     > - the code in expr_const_sets_add() used to be a relatively 
> direct
>         >     > translation from sets of constants to sets of port names/IPs; 
> this
>         >     > changes with the optimization your proposing.
>         >     > - the new utility, ovn-match-ip, would be useful but I'm 
> worried that
>         >     > users will often forget to use it.
>         >     I'd like to understand more about the benefits of this change, 
> what
>         >     problem is it solving? I agree with Dumitru that it will make 
> debugging
>         >     quite a bit more difficult (even with the new tool) so it would 
> be good
>         >     to understand the tradeoff?
>         > 
>         > The benefit is more due to the reduction of the flow table.
>         > 1. Fewer flow tables will install to ovs faster.
> 
>         Do you mean flows rather than flow tables?
> 
>     Yes. Sorry, my bad.
> 
>         > 2. Before optimization, enabling the openstack remote group feature 
> in a
>         > large-scale environment may reach the limit of 20,000 flow tables. 
> In the
>         > application of flows offload, this situation is more likely to 
> happen.
>         > 3. Fewer flow tables usually means faster flow table lookups and 
> stability.
>         > 4. When remote group is enabled, acl(security group) is usually a 
> challenge,
>         > and this challenge usually comes from a huge number of ip matching 
> entries.
>         > And this will reduce the challenge.
>         > 
> 
>         Ok I understand that the main benefit is performance. Do you have any
>         bench-marking figures? I am not trying to be obstructive, but this 
> will
>         change the user interface so I think it would be good to understand 
> the
>         benefit. In the end, it will be the maintainers who will need to 
> decide
>         if the benefit outweighs that change and should be applied. I do think
>         it will make things more difficult to debug problems in the flow table
> 
>     Yes, it is indeed harder to debug. The first version of patch included the
>     function of turning this feature on or off. In this way, users can turn on
>     this feature in a large-scale environment. By the way, after having this
>     ovn-match-ip tool, judging from the use in our own environment, it has
>     little effect on debugging. The function that can be turned on or off,
>     will it be more flexible?
> 
>     I have the approximate data on the reduction in the number of flows.
>     Of course, more benefits are based on the reduction of the flows.
>     In our own environment, in most cases, the flows will be reduced by more
>     than half. The larger the scale, the denser the IP address, the better
>     the effect of the flows reduction .

I had a conversation with Dumitru and this patch came up in the
conversation. He made an interesting suggestion (Dumitru, please correct
me if I get this wrong) that this could be refactored as an external
tool. This cmdline tool could, for example, take a set of IP addresses
and return the wildcard representation. For example,

# ./ovs-new-tool <set of addresses>
<ip address/prefix> ... <ip address/prefix>

Then this tool could optionally be used by a CMS. The other advantage of
this is that it could also be used in other places.
> 
>         >     > 
>         >     > I'm curious about your and the maintainers' opinion on these 
> points.
>         >     > 
>         >     > Another general remark is to increment the patch version when 
> submitting
>         >     > a new revision.  From what I can see this specific patch is 
> at v7.  When
>         >     > sending a v8 you could add "-v8" when generating the patch, 
> e.g.:
>         >     > 
>         >     > git format-patch --subject-prefix="PATCH ovn" -v8 -M 
> origin/master
>         >     > 
>         >     > Would generate a patch with subject:
>         >     > 
>         >     > "Subject: [PATCH ovn v8] ...."
>         >     > 
>         >     > This usually helps when reviewing patches that go through 
> multiple
>         >     > iterations.
>         >     > 
>         >     > More specific comments inline.
>         >     > 
>         >     >>  debian/ovn-common.install |   1 +
>         >     >>  lib/expr.c                | 219 
> ++++++++++++++++++++++++++++++++++++++
>         >     >>  rhel/ovn-fedora.spec.in   |   1 +
>         >     >>  tests/ovn.at              | 136 +++++++++++------------
>         >     >>  tests/test-ovn.c          |   9 ++
>         >     >>  utilities/automake.mk     |   5 +-
>         >     >>  utilities/ovn-match-ip.in |  78 ++++++++++++++
>         >     >>  7 files changed, 382 insertions(+), 67 deletions(-)
>         >     >>  create mode 100755 utilities/ovn-match-ip.in
>         >     >>
>         >     >> diff --git a/debian/ovn-common.install 
> b/debian/ovn-common.install
>         >     >> index 8e5915724..0095df918 100644
>         >     >> --- a/debian/ovn-common.install
>         >     >> +++ b/debian/ovn-common.install
>         >     >> @@ -5,6 +5,7 @@ usr/bin/ovn-ic-nbctl
>         >     >>  usr/bin/ovn-ic-sbctl
>         >     >>  usr/bin/ovn-trace
>         >     >>  usr/bin/ovn-detrace
>         >     >> +usr/bin/ovn-match-ip
>         >     >>  usr/share/ovn/scripts/ovn-ctl
>         >     >>  usr/share/ovn/scripts/ovndb-servers.ocf
>         >     >>  usr/share/ovn/scripts/ovn-lib
>         >     >> diff --git a/lib/expr.c b/lib/expr.c
>         >     >> index f061a8fbe..0a7203817 100644
>         >     >> --- a/lib/expr.c
>         >     >> +++ b/lib/expr.c
>         >     >> @@ -1060,6 +1060,200 @@ expr_constant_set_destroy(struct 
> expr_constant_set *cs)
>         >     >>      }
>         >     >>  }
>         >     >>  
>         >     >> +struct ip_in
>         >     >> +{
>         >     >> +    uint32_t *ip;
>         >     >> +    size_t size;
>         >     >> +};
>         >     > 
>         >     > I don't think we really need this, we could just pass the 
> IPv4 address
>         >     > array to combine_ipv4_in_mask(), along with the number of 
> items in the
>         >     > array.
>         >     > 
>         >     >> +
>         >     >> +struct ip_out_entry
>         >     >> +{
>         >     >> +    uint32_t ip;
>         >     >> +    uint32_t mask;
>         >     >> +    bool masked;
>         >     >> +};
>         >     >> +
>         >     >> +struct ip_out
>         >     >> +{
>         >     >> +    struct ip_out_entry *entries;
>         >     >> +    size_t used;
>         >     >> +    size_t size;
>         >     >> +};
>         >     > 
>         >     > AFAIU, we can never have more "out" entries than "in" 
> entries.  This
>         >     > means we could be conservative and always allocate as many 
> output
>         >     > entries as ip_in entries and pass the preallocated struct 
> ip_out_entry
>         >     > array to combine_ipv4_in_mask().
>         >     > 
>         >     > This would also remove the need to resize the "out" array.
>         >     > 
>         >     >> +
>         >     >> +static int
>         >     >> +compare_mask_ip(const void *a, const void *b)
>         >     > 
>         >     > The implementation doesn't really use the fact that 'a' and 
> 'b' are IPs.
>         >     >  They're just compared as integers.  We might want to use a 
> different
>         >     > name and move this function to a common module as it could 
> potentially
>         >     > be used in multiple places in the future.
>         >     > 
>         >     >> +{
>         >     >> +    uint32_t a_ = *(uint32_t *)a;
>         >     >> +    uint32_t b_ = *(uint32_t *)b;
>         >     >> +
>         >     >> +    return a_ < b_ ? -1 : a_ > b_;
>         >     >> +}
>         >     >> +
>         >     >> +/* Function to check ip return data and xrealloc. */
>         >     >> +static void
>         >     >> +check_realloc_ip_out(struct ip_out *ip_out_data){
>         >     >> +    if (ip_out_data->used >= ip_out_data->size) {
>         >     >> +        ip_out_data->entries = 
> x2nrealloc(ip_out_data->entries,
>         >     >> +                                    &ip_out_data->size,
>         >     >> +                                    sizeof 
> *ip_out_data->entries);
>         >     >> +    }
>         >     >> +}
>         >     > 
>         >     > As mentioned in the comment earlier, we can probably avoid 
> using this
>         >     > function.
>         >     > 
>         >     >> +
>         >     >> +/* Simple description of the algorithm.
>         >     >> + * There are two situations
>         >     >> + * 1. Combine once
>         >     >> + * such as:
>         >     >> + *     1.1.1.0 1.1.1.1 1.0.1.0 1.0.1.1
>         >     >> + *     Combined into: 1.1.1.0/31, 1.0.1.0/31
>         >     >> + * 2. Combine multiple times
>         >     >> + *     1.1.1.0 1.1.1.1 1.0.1.0 1.0.1.1
>         >     >> + *     Combined into: 1.0.1.0/255.254.255.254
>         >     >> + *
>         >     >> + * Considering the actual scene and simplicity, the first 
> case is used to
>         >     >> + * combine once.
>         >     >> + *
>         >     >> + * ...00...
>         >     >> + * ...01...
>         >     >> + * ...10...
>         >     >> + * ...11...
>         >     >> + * "..." means the same value omitted.
>         >     >> + * Obviously, the above value can be expressed as 
> ...00.../11100111. This
>         >     >> + * continuous interval that can be represented by one or 
> several wildcard
>         >     >> + * masks is called a segment.
>         >     >> + * Only if all 2<<n values from the minimum value 00...(n) 
> to 11...(n)
>         >     >> + * exist, can they be combined into 00...(n)/00...( n)
>         >     >> + *
>         >     >> + * First sort all the values by size. Iterate through each 
> value.
>         >     >> + * 1. Find a new segment, where two values differ only by 1 
> bit, such as
>         >     >> + * ...0... and ...1...
>         >     >> + *     diff = ip_next ^ ip
>         >     >> + *     if (diff & (diff-1)) == 0
>         >     >> + *         new_segment = true
>         >     >> + * The first non-zero place in the high direction of ip is 
> the end of the
>         >     >> + * segment(segment_end).
>         >     >> + * For example...100... and...101..., the segment_end is 
> ...111...
>         >     >> + *
>         >     >> + * 2. Count the number of consecutive and less than 
> continuous_size in the
>         >     >> + * segment.
>         >     >> + *     diff = ip_next - ip
>         >     >> + *     if (diff & (diff-1)) == 0 && ip_next <= segment_end
>         >     >> + *         continuous_size++
>         >     >> + *
>         >     >> + * 3. Combine different ip intervals in the segment 
> according to
>         >     >> + * continuous_size.
>         >     >> + * In continuous_size, from the highest bit of 1 to the 
> lowest bit of 1, in
>         >     >> + * the order of segment start, each bit that is 1 is a 
> different ip interval
>         >     >> + * that can be combined with a wildcard mask.
>         >     >> + * For example, 000, 001, 010:
>         >     >> + *     continuous_size: 3 (binary 11), segment_start: 000
>         >     >> + *     mask: ~(1 << 1 - 1) = 110; ~(1 << 0 - 1) = 111;
>         >     >> + *     Combined to: 000/110, 010/111
>         >     >> + *
>         >     >> + * 4. The ip that cannot be recorded in a segment will not 
> be combined. */
>         >     > 
>         >     > Thanks for adding the detailed description here.  However, 
> it's a bit
>         >     > confusing because the names used in the description don't 
> match the
>         >     > variable names used in the implementation below.  Maybe it's 
> better to
>         >     > not use names in the description?
>         > 
>         >     I would prefer that with some refactoring of the code below and 
> some
>         >     in-line comments, we could make the documentation unnecessary 
> and remove
>         >     or reduce it.
>         > 
>         > Yes, less in-line comments will be better.
>         > 
>         >     > 
>         >     >> +static void
>         >     >> +combine_ipv4_in_mask(struct ip_in *ip_in_data, struct 
> ip_out *ip_out_data){
>         >     > 
>         >     > Please move the curly brace on a new line.
>         >     > 
>         >     >> +    bool recording = false;
>         >     >> +    uint32_t i, diff, connect, start, end, continuous_size, 
> mask_base;
>         >     > 
>         >     > Some of these can be moved in the specific blocks where 
> they're used.
>         >     > 
>         >     >> +
>         >     >> +    start = 0;
>         >     >> +    continuous_size = 0;
>         >     >> +    mask_base = 0;
>         >     >> +    end = 0;
>         >     >> +
>         >     >> +    qsort(ip_in_data->ip, ip_in_data->size, 
> sizeof(uint32_t),
>         >     > 
>         >     > Please use "sizeof *ip_in_data->ip".
>         >     > 
>         >     >> +          compare_mask_ip);
>         >     >> +    memset(ip_out_data, 0, sizeof(struct ip_out));
>         >     >> +
>         >     >> +    for (i = 0; i < ip_in_data->size; i++) {
>         >     >> +        if (i + 1 >= ip_in_data->size) {
>         >     >> +            if (!recording) {
>         >     >> +                goto end_when_not_recording;
>         >     >> +            } else {
>         >     >> +                goto end_when_recording;
>         >     >> +            }
>         > 
>         >     At a first glance, I don't know what "recording" is. Maybe it 
> needs
>         >     another name ("combining" or even "new_segment" as you have in 
> the
>         >     documentation?) or some comments.
>         > 
>         > Yes,  the combining name will be more readable.
>         > 
>         >     > 
>         >     > IMO it makes the code very hard to read if we have gotos 
> jumping inside
>         >     > if/else branches or inside loops.  This is also usually a 
> sign that
>         >     > we're trying to avoid code duplication.  A potentially 
> simpler and more
>         >     > readable way to do that is to add functions for the specific 
> operations
>         >     > we don't want to duplicate.
>         >     > 
>         >     >> +        }
>         >     >> +        /* Not recording in a segment.
>         >     >> +         * Record a new segment or not combine. */
>         >     >> +        if (!recording) {
>         >     >> +            connect = ip_in_data->ip[i + 1] ^ 
> ip_in_data->ip[i];
>         > 
>         >     Maybe this ^ could be a macro as it would help explain why you 
> are
>         >     XORing these two IP addresses.
>         > 
>         >     >> +            /* Only one bit different. */
>         >     >> +            if ((connect & (connect - 1)) == 0) {
>         > 
>         >     Maybe you could use 'count_1bits()' as it would make it clearer
>         > 
>         >     How about something like:
>         > 
>         >     #define COUNT_BITS_DIFFERENT (uint32_t a, uint32_t b) 
> count_1bits((a) ^ (b))
>         > 
>         >     and
>         >     if (COUNT_BITS_DIFFERENT(...) == 1)
>         > 
>         >     What do you think?
>         > 
>         > static inline bool
>         > is_1bit_different(uint32_t a, uint32_t b)
>         > {
>         >     return (a ^ b) & (a ^ b - 1) == 0;
>         > }
>         > 
>         > Or add is_1bit_different to ovs/util.h and use is_1bit_different() 
> which
>         > one do you think is better?
> 
>         That would be good as well. The main reason is to make the code easier
>         to understand. Its a complex algorithm and I found it difficult to
>         follow so I think it should be refactored until it
>         is easier to understand so this patch may need to go through a few
>         iterations. If it is easier to understand, it will be easier to 
> maintain.
> 
>     Ok, I will make the code more clear and easier to read.
> 
>         > 
>         >     >> +                recording = true;
>         >     >> +                start = ip_in_data->ip[i];
>         >     >> +                continuous_size = 2;
>         >     >> +                mask_base = connect;
>         >     >> +
>         >     >> +                int j = 0;
>         >     >> +                end = start;
>         > 
>         >     Maybe the name could be changed to 'max' or something like that 
> as it is
>         >     not the actual end but the maximum possible ip in the segment?
>         > 
>         > Yes, max will be better.
>         > 
>         >     >> +                /* The first non-zero place in the high 
> direction is
>         >     >> +                 * the end of the segment. *
>         >     Maybe bitwise_scan() could be used to scan for the bit that it 
> is set?
>         > 
>         > When using bitwise_scan() , it might be like: 
>         > 
>         > int ofs_base, ofs_max
>         > 
>         > ofs_base = bitwise_scan(&start, 32, true, 0, 32);
>         So this will find the position of the first 'set' bit?
>         > if(ofs_base >= 31){
>         >     max = 1 << 31 | start;
>         > } else {
>         >     ofs_max = bitwise_scan(&start, 32, true, ofs_base + 1, 32);
>         This will find the position of the the second 'set' bit?
> 
>         This means we are looking for the largest pattern like this?
>         11
>         101
>         1001
>         10001
>         ..
>         ..
> 
>     Yes, because each continuous segment must start at...000..., we will find 
> the

This is a good clarification to put in the definition of a segment (i.e.
starts at 0..0). So you are looking for the longest string of zeros in a
segment.
>     longest and highest 0. For example, 1000, 1001, starting from 1000, the
>     largest segment that can be merged starting from 1000, but cannot exceed
>     1111 (that is, max), because the high 1 in 1000 is not 0. Similarly,
>     101000, 101001..., the segment starting from 10100, the maximum cannot
>     exceed 10111 (that is, max)
> 
>     101000/111000
>     contain
> 
>     101000
>     ...
>     101111
> 
>     101000 is the beginning of this 1000/1000 segment.
>     101111 is the maximum value of this segment.

It is a difficult algorithm to grasp (and hence document) but I think I
get it now.
> 
>         >     max = start | ((1 << (ofs_max - ofs_base) - 1) << ofs_base)
>         And this will build a mask between those two bit positions and then
>         apply to 'start'?
> 
>     Yes, this is to set the 1 corresponding to the mask to start, that is, the
>     longest ip 0 at the beginning of this segment is set to 1, which becomes
>     the largest ip value of this segment.
> 
> Yes, because each continuous segment must start at...000..., we will find the
> longest and highest 0. For example, 1000, 1001, starting from 1000, the
> largest segment that can be merged starting from 1000, but cannot exceed
> 1111 (that is, max), because the high 1 in 1000 is not 0. Similarly,
> 101000, 101001..., the segment starting from 101000, the maximum cannot
> exceed 101111 (that is, max)
> 
> 101000/111000
> contain
> 
> 101000
> ...
> 101111
> 
> 101000 is the beginning of this 101000/101000 segment.
> 101111 is the maximum value of this segment.
> 
>         >     max = start | ((1 << (ofs_max - ofs_base) - 1) << ofs_base)
>         And this will build a mask between those two bit positions and then
>         apply to 'start'?
> 
>     Yes, this is to set the 1 corresponding to the mask to start, that is, the
>     longest ip 0 at the beginning of this segment is set to 1, which becomes
>     the largest ip value of this segment.

Does the largest ip value not have all ones?
> 
>         > }
>         > 
>         > Is this more complicated, do you think?
>         IMO, seemed a bit easier to understand.
> 
>     This is really easier to understand. But the code structure is a bit more
>     cumbersome. How about adding this example
>     '''
>     101000/111000
>     contain
> 
>     101000
>     ...
>     101111
> 
>     101000 is the beginning of this 1000/1000 segment.
> 
> correct-->  101000 is the beginning of this 101000/101000 segment.
> 
>     101111 is the maximum value of this segment.
>     '''
>     to the comment of this while code? Will it be easier to read? Because I
>     think the while code will be more efficient.

Personally, I am not really concerned about the efficiency of the code
as it is on the control plane. I would rather the code was simple to
understand quickly. I want to make sure that if I (or someone else) was
looking at this code in a year or two from now they could easily see
what was going on.
> 
>         > 
>         >     >> +                while (j < 32 && ((start & (mask_base << 
> j)) == 0)) {
>         >     >> +                    end |= (mask_base << j);
>         >     >> +                    j++;
>         >     >> +                }
>         > 
>         >     I think this could use a comment to explain why this is the 
> case. I kind
>         >     of get it but it took me a while and I am still not 100% sure. 
> As I
>         >     mentioned elsewhere, I think this is really looking for the 
> maximum
>         >     potential IP address rather than what will actually be the the 
> end value.
>         > 
>         > Yes, this is looking for the maximum potential IP address.
>         > I will add a comment like:
>         > This is looking for the maximum potential IP address. Used to 
> terminate
>         > the combining of a segment
> 
>         Maybe adding more formalized language describing 'segments' and then
>         referring to this consistently in the code and comments would help. 
> For
>         example, i think this description is true:
> 
>         --
>         A 'segment' is described by:
>         1) A 'segment_mask' which is a bit mask of length 'segment_mask_len'
>         bits that begins at a bit offset of 'segment_mask_offset'.
>         2) A 'segment_id' which uniquely identifies a 'segment' for a given
>         'segment_mask'. An IP address is a member of a 'segment' if it belongs
>         to that 'segment_id'. The 'segment_id' is obtained by a bitwise AND of
>         the inverse of the 'segment_mask' with that IP address. This is 
> similar
>         to the Network ID of an IPv4 address.
> 
>         A set of 'segment's can be combined if all IP addresses in the segment
>         are present in the set.
>         --
> 
>         To describe the above code snippet, you could state something like:
> 
>         --
>         'segment_max' is the numerically largest IP address in a 'segment'. It
>         can be found by a bitwise OR of the 'segment_mask' with the
>         'segment_id'. In order to find the largest possible 'segment', we
>         attempt to find the widest 'segment_mask' by ...
>         ...
>         ..
> 
> 
>         What do you think?
> 
>     Amazing! I will add the description of segments to the comments, thanks!
> 
>         > 
>         >     >> +
>         >     >> +                continue;
>         >     >> +            /* Different segments and different bit, dnot 
> combine. */
>         >     >> +            } else {
>         >     >> +end_when_not_recording:
>         >     > 
>         >     > Same here.
>         >     > 
>         >     >> +                check_realloc_ip_out(ip_out_data);
>         >     >> +
>         >     >> +                ip_out_data->entries[ip_out_data->used].ip =
>         >     >> +                    ip_in_data->ip[i];
>         >     >> +                
> ip_out_data->entries[ip_out_data->used].masked = false;
>         >     >> +                ip_out_data->used++;
>         >     >> +
>         >     >> +                continue;
>         >     >> +            }
>         >     >> +        /* Recording in the current segment. */
>         >     >> +        } else {
>         >     >> +            diff = ip_in_data->ip[i + 1] - 
> ip_in_data->ip[i];
>         >     >> +            /* Ignore equal node. */
>         >     >> +            if (diff == 0) {
>         >     >> +                continue;
>         >     >> +            }
>         > 
>         >     Why would they be equal? And if they were, should we not check 
> this in
>         >     the non-recording case above as well?
>         > 
>         > Under normal circumstances, it will not be equal, here is to deal 
> with
>         > incorrect equality.
>         Maybe add a comment to explain.
>     Ok.
>         > In non-recording case, it also needs to be checked, my bad.
>         > 
>         >     >> +            /* Stop recording and combine, or continue 
> recording. */
>         >     >> +            if (((diff & (diff - 1)) != 0)
>         >     >> +                || (ip_in_data->ip[i + 1] > end)) {
>         >     >> +end_when_recording:
>         >     > 
>         >     > Here too.
>         >     > 
>         >     >> +                recording = false;
>         >     >> +                while (continuous_size) {
>         >     >> +                    check_realloc_ip_out(ip_out_data);
>         >     >> +
>         >     >> +                    int segment_power, pow_base;
>         >     >> +                    if (continuous_size == 0) {
>         >     >> +                        segment_power = 0;
>         >     >> +                    } else {
>         >     >> +                        segment_power = 31 - 
> clz32(continuous_size);
>         >     >> +                    }
>         >     >> +
>         >     >> +                    if (mask_base == 0) {
>         >     >> +                        pow_base = 0;
>         >     >> +                    } else {
>         >     >> +                        pow_base = 31 - clz32(mask_base);
>         >     >> +                    }
>         >     >> +
>         >     >> +                    
> ip_out_data->entries[ip_out_data->used].mask =
>         >     >> +                        ~(((1 << segment_power) - 1) << 
> pow_base);
>         > 
>         >     Maybe bitwise_one() could be used to build the mask.
>         > 
>         >     As a general comment, it can take some time to reason what bit
>         >     manipulation code is actually doing. I think it would be good 
> to use
>         >     some of the OVS/OVN bit manipulation helper functions anywhere 
> that is
>         >     appropriate in this code. This will improve the readability.
>         > 
>         > Yes, bitwise_one() is better.
>         > Thanks, I try to use OVS/OVN bit manipulation helper functions.
>         > 
>         >     >> +                    
> ip_out_data->entries[ip_out_data->used].ip =
>         >     >> +                        
> ip_out_data->entries[ip_out_data->used].mask
>         >     >> +                        & start;
>         >     >> +                    
> ip_out_data->entries[ip_out_data->used].masked = true;
>         >     >> +                    ip_out_data->used++;
>         >     >> +
>         >     >> +                    continuous_size &= (~(1 << 
> segment_power));
>         >     >> +                    start = 
> ip_out_data->entries[ip_out_data->used - 1].ip
>         >     >> +                            + (1 << (segment_power + 
> pow_base));
>         > 
>         >     Could you move 'ip_out_data->used++' to here and change the 
> index above
>         >     ^ to 'ip_out_data->used'?
>         > 
>         > Ok, thanks.
>         > 
>         >     >> +                }
>         >     >> +            } else {
>         >     >> +                continuous_size++;
>         > 
>         >     Maybe the code in the 'if' statement could be simplified by 
> calculating
>         >     the 'pow_base' and 'segment_power' here at each iteration? I'm 
> not sure
>         >     but I suspect it might at least remove the 
> 'while(continuous_size)' loop?
>         > 
>         > Sorry, I don’t know if I understand you correctly. As dumit 
> suggested, the
>         > while statement will be implemented with a function to eliminate 
> the goto
>         > statement.
> 
>         Apologies for my vague comment, let's see how Dumitru's suggestion 
> looks
>         and we can work from there.
> 
>         > I am not sure how to delete the "while(continuous_size)" loop.
>         > 
>         > But I noticed that 
>         >     if (continuous_size == 0) {
>         >         segment_power = 0;
>         >     } else {
>         >         segment_power = 31 - clz32(continuous_size);
>         >     }
>         > can be replaced by 
>         >         segment_power = 31 - clz32(continuous_size);
>         > 
>         > And
>         >     if (mask_base == 0) {
>         >         pow_base = 0;
>         >     } else {
>         >         pow_base = 31 - clz32(mask_base);
>         >     }
>         > can be moved outside of while.
>         > 
>         > do you mean this?
> 
>         I didnt but I think that is good, lets try to minimize the complexity 
> by
>         removing as many loops, jumps and branches as we can
> 
>     Ok.
> 
>         > 
>         >     >> +            }
>         >     >> +
>         >     >> +            continue;
>         >     >> +        }
>         >     >> +    }
>         >     >> +}
>         >     >> +
>         >     >>  /* Adds an constant set named 'name' to 'const_sets', 
> replacing any existing
>         >     >>   * constant set entry with the given name. */
>         >     >>  void
>         >     >> @@ -1074,6 +1268,11 @@ expr_const_sets_add(struct shash 
> *const_sets, const char *name,
>         >     >>      cs->in_curlies = true;
>         >     >>      cs->n_values = 0;
>         >     >>      cs->values = xmalloc(n_values * sizeof *cs->values);
>         >     >> +    struct ip_out ip_out_data;
>         >     >> +    struct ip_in ip_in_data;
>         >     >> +    ip_in_data.ip = xmalloc(n_values * sizeof(uint32_t));
>         >     >> +    ip_in_data.size = 0;
>         >     >> +
>         >     >>      if (convert_to_integer) {
>         >     >>          cs->type = EXPR_C_INTEGER;
>         >     >>          for (size_t i = 0; i < n_values; i++) {
>         >     >> @@ -1086,6 +1285,9 @@ expr_const_sets_add(struct shash 
> *const_sets, const char *name,
>         >     >>                  && lex.token.type != LEX_T_MASKED_INTEGER) {
>         >     >>                  VLOG_WARN("Invalid constant set entry: 
> '%s', token type: %d",
>         >     >>                            values[i], lex.token.type);
>         >     >> +            } else if (lex.token.type == LEX_T_INTEGER
>         >     >> +                       && lex.token.format == LEX_F_IPV4) {
>         >     >> +                ip_in_data.ip[ip_in_data.size++] = 
> ntohl(lex.token.value.ipv4);
>         >     >>              } else {
>         > 
>         >     Seems like it would also need an IPv6 version.
>         > 
>         >     Thinking about this a bit more, why does this need to be 
> specific to
>         >     IPv4 - surely there is a similar problem across all masked 
> integers?
>         > 
>         > Considering that ipv6 does not have a concept like ipv4 mask, and 
> private
>         > networks use ipv6 is relatively small, so I did not consider ipv6.
>         > Of course, this algorithm is also suitable for ipv6 in addition to 
> the
>         > uint32_t type. My idea is to add ipv6 part of the support when 
> encountering
>         > this kind of scene in the ipv6 scene. what do you think?
> 
>         I guess its up to the maintainers.
> 
>     If ipv6 is necessary, I can expand the type size to support ipv6.
> 
>     Thanks so much,
>     Gongming
> 
>         > 
>         > Thanks,
>         > Gongming
>         > 
>         >     >>                  union expr_constant *c = 
> &cs->values[cs->n_values++];
>         >     >>                  c->value = lex.token.value;
>         >     >> @@ -1105,6 +1307,23 @@ expr_const_sets_add(struct shash 
> *const_sets, const char *name,
>         >     >>          }
>         >     >>      }
>         >     >>  
>         >     >> +    if (ip_in_data.size > 0) {
>         >     >> +        combine_ipv4_in_mask(&ip_in_data, &ip_out_data);
>         >     >> +        for (size_t i = 0; i < ip_out_data.used; ++i) {
>         >     >> +            union expr_constant *c = 
> &cs->values[cs->n_values++];
>         >     >> +            memset(&c->value, 0, sizeof c->value);
>         >     >> +            memset(&c->mask, 0, sizeof c->mask);
>         >     >> +            c->value.ipv4 = 
> htonl(ip_out_data.entries[i].ip);
>         >     >> +            c->format = LEX_F_IPV4;
>         >     >> +            c->masked = ip_out_data.entries[i].masked;
>         >     >> +            if (c->masked) {
>         >     >> +                c->mask.ipv4 = 
> htonl(ip_out_data.entries[i].mask);
>         >     >> +            }
>         >     >> +        }
>         >     >> +        free(ip_out_data.entries);
>         >     >> +    }
>         >     >> +
>         >     >> +    free(ip_in_data.ip);
>         >     >>      shash_add(const_sets, name, cs);
>         >     >>  }
>         >     >>  
>         >     >> diff --git a/rhel/ovn-fedora.spec.in 
> b/rhel/ovn-fedora.spec.in
>         >     >> index 6716dd0d2..1a0abff06 100644
>         >     >> --- a/rhel/ovn-fedora.spec.in
>         >     >> +++ b/rhel/ovn-fedora.spec.in
>         >     >> @@ -455,6 +455,7 @@ fi
>         >     >>  %{_bindir}/ovn-sbctl
>         >     >>  %{_bindir}/ovn-trace
>         >     >>  %{_bindir}/ovn-detrace
>         >     >> +%{_bindir}/ovn-match-ip
>         >     >>  %{_bindir}/ovn-appctl
>         >     >>  %{_bindir}/ovn-ic-nbctl
>         >     >>  %{_bindir}/ovn-ic-sbctl
>         >     >> diff --git a/tests/ovn.at b/tests/ovn.at
>         >     >> index b751d6db2..a243cd6f4 100644
>         >     >> --- a/tests/ovn.at
>         >     >> +++ b/tests/ovn.at
>         >     >> @@ -618,20 +618,32 @@ ip,nw_src=10.0.0.3
>         >     >>  ])
>         >     >>  AT_CHECK([expr_to_flow 'ip4.src == $set1'], [0], [dnl
>         >     >>  ip,nw_src=10.0.0.1
>         >     >> -ip,nw_src=10.0.0.2
>         >     >> -ip,nw_src=10.0.0.3
>         >     >> +ip,nw_src=10.0.0.2/31
>         >     >> +])
>         >     >> +AT_CHECK([expr_to_flow 'ip4.src == $set5'], [0], [dnl
>         >     >> +ip,nw_src=1.1.1.128/26
>         >     >> +ip,nw_src=1.1.1.16/28
>         >     >> +ip,nw_src=1.1.1.192/27
>         >     >> +ip,nw_src=1.1.1.2/31
>         >     >> +ip,nw_src=1.1.1.224/28
>         >     >> +ip,nw_src=1.1.1.240/29
>         >     >> +ip,nw_src=1.1.1.248/30
>         >     >> +ip,nw_src=1.1.1.252/31
>         >     >> +ip,nw_src=1.1.1.254
>         >     >> +ip,nw_src=1.1.1.32/27
>         >     >> +ip,nw_src=1.1.1.4/30
>         >     >> +ip,nw_src=1.1.1.64/26
>         >     >> +ip,nw_src=1.1.1.8/29
>         >     >>  ])
>         >     >>  AT_CHECK([expr_to_flow 'ip4.src == {1.2.3.4, $set1}'], [0], 
> [dnl
>         >     >>  ip,nw_src=1.2.3.4
>         >     >>  ip,nw_src=10.0.0.1
>         >     >> -ip,nw_src=10.0.0.2
>         >     >> -ip,nw_src=10.0.0.3
>         >     >> +ip,nw_src=10.0.0.2/31
>         >     >>  ])
>         >     >>  AT_CHECK([expr_to_flow 'ip4.src == {1.2.0.0/20, 5.5.5.0/24, 
> $set1}'], [0], [dnl
>         >     >>  ip,nw_src=1.2.0.0/20
>         >     >>  ip,nw_src=10.0.0.1
>         >     >> -ip,nw_src=10.0.0.2
>         >     >> -ip,nw_src=10.0.0.3
>         >     >> +ip,nw_src=10.0.0.2/31
>         >     >>  ip,nw_src=5.5.5.0/24
>         >     >>  ])
>         >     >>  AT_CHECK([expr_to_flow 'ip6.src == {::1, ::2, ::3}'], [0], 
> [dnl
>         >     >> @@ -13768,23 +13780,20 @@ cat 2.expected > expout
>         >     >>  $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" 
> hv1/vif2-tx.pcap > 2.packets
>         >     >>  AT_CHECK([cat 2.packets], [0], [expout])
>         >     >>  
>         >     >> -# There should be total of 9 flows present with conjunction 
> action and 2 flows
>         >     >> +# There should be total of 6 flows present with conjunction 
> action and 2 flows
>         >     >>  # with conj match. Eg.
>         >     >> -# table=44, priority=2001,conj_id=2,metadata=0x1 
> actions=resubmit(,45)
>         >     >> -# table=44, priority=2001,conj_id=3,metadata=0x1 
> actions=drop
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.6 
> actions=conjunction(2,2/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.4 
> actions=conjunction(2,2/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.5 
> actions=conjunction(2,2/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.7 
> actions=conjunction(3,2/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.9 
> actions=conjunction(3,2/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.8 
> actions=conjunction(3,2/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_src=10.0.0.6 
> actions=conjunction(2,1/2),conjunction(3,1/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_src=10.0.0.4 
> actions=conjunction(2,1/2),conjunction(3,1/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_src=10.0.0.5 
> actions=conjunction(2,1/2),conjunction(3,1/2)
>         >     >> -
>         >     >> -OVS_WAIT_UNTIL([test 9 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >> +#table=45, n_packets=1, n_bytes=42, idle_age=211, 
> priority=2001,conj_id=2,ip,metadata=0x1 actions=resubmit(,46)
>         >     >> +#table=45, n_packets=0, n_bytes=0, idle_age=211, 
> priority=2001,conj_id=3,ip,metadata=0x1 actions=drop
>         >     >> +#priority=2001,ip,metadata=0x1,nw_src=10.0.0.6 
> actions=conjunction(2,2/2),conjunction(3,2/2)
>         >     >> +#priority=2001,ip,metadata=0x1,nw_src=10.0.0.4/31 
> actions=conjunction(2,2/2),conjunction(3,2/2)
>         >     >> +#priority=2001,ip,metadata=0x1,nw_dst=10.0.0.4/31 
> actions=conjunction(2,1/2)
>         >     >> +#priority=2001,ip,metadata=0x1,nw_dst=10.0.0.6 
> actions=conjunction(2,1/2)
>         >     >> +#priority=2001,ip,metadata=0x1,nw_dst=10.0.0.7 
> actions=conjunction(3,1/2)
>         >     >> +#priority=2001,ip,metadata=0x1,nw_dst=10.0.0.8/31 
> actions=conjunction(3,1/2)
>         >     >> +
>         >     >> +OVS_WAIT_UNTIL([test 6 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >>  grep conjunction | wc -l`])
>         >     >> -OVS_WAIT_UNTIL([test 3 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >> +OVS_WAIT_UNTIL([test 2 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >>  grep conjunction.*conjunction | wc -l`])
>         >     >>  OVS_WAIT_UNTIL([test 2 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >>  grep conj_id | wc -l`])
>         >     >> @@ -13811,18 +13820,16 @@ AT_CHECK([cat 2.packets], [0], [])
>         >     >>  # properly.
>         >     >>  # There should be total of 6 flows present with conjunction 
> action and 1 flow
>         >     >>  # with conj match. Eg.
>         >     >> -# table=44, priority=2001,conj_id=3,metadata=0x1 
> actions=drop
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.7 
> actions=conjunction(4,2/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.9 
> actions=conjunction(4,2/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.8 
> actions=conjunction(4,2/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_src=10.0.0.6 
> actions=conjunction(4,1/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_src=10.0.0.4 
> actions=conjunction(4,1/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_src=10.0.0.5 
> actions=conjunction(4,1/2)
>         >     >> +# table=45, n_packets=1, n_bytes=42, idle_age=34, 
> priority=2001,conj_id=3,ip,metadata=0x1 actions=drop
>         >     >> +# priority=2001,ip,metadata=0x1,nw_src=10.0.0.6 
> actions=conjunction(3,2/2)
>         >     >> +# priority=2001,ip,metadata=0x1,nw_src=10.0.0.4/31 
> actions=conjunction(3,2/2)
>         >     >> +# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.8/31 
> actions=conjunction(3,1/2)
>         >     >> +# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.7 
> actions=conjunction(3,1/2)
>         >     >>  
>         >     >>  ovn-nbctl acl-del ls1 to-lport 1001 \
>         >     >>  'ip4 && ip4.src == $set1 && ip4.dst == $set1'
>         >     >>  
>         >     >> -OVS_WAIT_UNTIL([test 6 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >> +OVS_WAIT_UNTIL([test 4 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >>  grep conjunction | wc -l`])
>         >     >>  OVS_WAIT_UNTIL([test 0 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >>  grep conjunction.*conjunction | wc -l`])
>         >     >> @@ -13836,43 +13843,40 @@ ovn-nbctl acl-add ls1 to-lport 
> 1001 \
>         >     >>  ovn-nbctl acl-add ls1 to-lport 1001 \
>         >     >>  'ip4 && ip4.src == $set1 && ip4.dst == {10.0.0.9, 
> 10.0.0.10}' drop
>         >     >>  
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.8 
> actions=conjunction(4,1/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.7 
> actions=conjunction(4,1/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.4 
> actions=conjunction(5,1/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.5 
> actions=conjunction(5,1/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.6 
> actions=conjunction(5,1/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.9 
> actions=conjunction(4,1/2),conjunction(6,1/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.10 
> actions=conjunction(6,1/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_src=10.0.0.5 
> actions=conjunction(4,2/2),conjunction(5,2/2),conjunction(6,2/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_src=10.0.0.4 
> actions=conjunction(4,2/2),conjunction(5,2/2),conjunction(6,2/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_src=10.0.0.6 
> actions=conjunction(4,2/2),conjunction(5,2/2),conjunction(6,2/2)
>         >     >> -
>         >     >> -OVS_WAIT_UNTIL([test 10 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >> +# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.7 
> actions=conjunction(3,1/2)
>         >     >> +# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.8/31 
> actions=conjunction(3,1/2)
>         >     >> +# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.4/31 
> actions=conjunction(4,1/2)
>         >     >> +# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.6 
> actions=conjunction(4,1/2)
>         >     >> +# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.9 
> actions=conjunction(5,1/2)
>         >     >> +# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.10 
> actions=conjunction(5,1/2)
>         >     >> +# priority=2001,ip,metadata=0x1,nw_src=10.0.0.4/31 
> actions=conjunction(3,2/2),conjunction(4,2/2),conjunction(5,2/2)
>         >     >> +# priority=2001,ip,metadata=0x1,nw_src=10.0.0.6 
> actions=conjunction(3,2/2),conjunction(4,2/2),conjunction(5,2/2)
>         >     >> +
>         >     >> +OVS_WAIT_UNTIL([test 8 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >>  grep conjunction | wc -l`])
>         >     >> -OVS_WAIT_UNTIL([test 4 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >> +OVS_WAIT_UNTIL([test 2 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >>  grep conjunction.*conjunction | wc -l`])
>         >     >> -OVS_WAIT_UNTIL([test 3 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >> +OVS_WAIT_UNTIL([test 2 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >>  grep conjunction.*conjunction.*conjunction | wc -l`])
>         >     >>  
>         >     >>  # Remove 10.0.0.7 from address set2. All flows should be 
> updated properly.
>         >     >>  ovn-nbctl set Address_Set set2 \
>         >     >>  addresses=\"10.0.0.8\",\"10.0.0.9\"
>         >     >>  
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.4 
> actions=conjunction(9,1/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.10 
> actions=conjunction(7,1/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.8 
> actions=conjunction(8,1/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.5 
> actions=conjunction(9,1/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.9 
> actions=conjunction(7,1/2),conjunction(8,1/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.6 
> actions=conjunction(9,1/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_src=10.0.0.5 
> actions=conjunction(7,2/2),conjunction(8,2/2),conjunction(9,2/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_src=10.0.0.6 
> actions=conjunction(7,2/2),conjunction(8,2/2),conjunction(9,2/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_src=10.0.0.4 
> actions=conjunction(7,2/2),conjunction(8,2/2),conjunction(9,2/2)
>         >     >> -
>         >     >> -OVS_WAIT_UNTIL([test 9 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >> +# 
> priority=2001,ip,metadata=0x1,nw_src=10.0.0.4/31,nw_dst=10.0.0.8/31 
> actions=drop
>         >     >> +# 
> priority=2001,ip,metadata=0x1,nw_src=10.0.0.6,nw_dst=10.0.0.8/31 actions=drop
>         >     >> +# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.4/31 
> actions=conjunction(4,1/2)
>         >     >> +# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.6 
> actions=conjunction(4,1/2)
>         >     >> +# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.9 
> actions=conjunction(5,1/2)
>         >     >> +# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.10 
> actions=conjunction(5,1/2)
>         >     >> +# priority=2001,ip,metadata=0x1,nw_src=10.0.0.6 
> actions=conjunction(5,2/2),conjunction(4,2/2)
>         >     >> +# priority=2001,ip,metadata=0x1,nw_src=10.0.0.4/31 
> actions=conjunction(5,2/2),conjunction(4,2/2)
>         >     >> +
>         >     >> +OVS_WAIT_UNTIL([test 6 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >>  grep conjunction | wc -l`])
>         >     >> -OVS_WAIT_UNTIL([test 4 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >> +OVS_WAIT_UNTIL([test 2 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >>  grep conjunction.*conjunction | wc -l`])
>         >     >> -OVS_WAIT_UNTIL([test 3 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >> +OVS_WAIT_UNTIL([test 0 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >>  grep conjunction.*conjunction.*conjunction | wc -l`])
>         >     >>  
>         >     >>  # Remove an ACL again
>         >     >> @@ -13881,16 +13885,16 @@ ovn-nbctl acl-del ls1 to-lport 
> 1001 \
>         >     >>  
>         >     >>  wait_for_ports_up
>         >     >>  ovn-nbctl --wait=hv sync
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.10 
> actions=conjunction(10,1/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.8 
> actions=conjunction(11,1/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.9 
> actions=conjunction(10,1/2),conjunction(11,1/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_src=10.0.0.5 
> actions=conjunction(10,2/2),conjunction(11,2/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_src=10.0.0.6 
> actions=conjunction(10,2/2),conjunction(11,2/2)
>         >     >> -# priority=2001,ip,metadata=0x1,nw_src=10.0.0.4 
> actions=conjunction(10,2/2),conjunction(11,2/2)
>         >     >> +# 
> priority=2001,ip,metadata=0x1,nw_src=10.0.0.6,nw_dst=10.0.0.8/31 actions=drop
>         >     >> +# 
> priority=2001,ip,metadata=0x1,nw_src=10.0.0.4/31,nw_dst=10.0.0.8/31 
> actions=drop
>         >     >> +# priority=2001,ip,metadata=0x1,nw_src=10.0.0.6 
> actions=conjunction(5,2/2)
>         >     >> +# priority=2001,ip,metadata=0x1,nw_src=10.0.0.4/31 
> actions=conjunction(5,2/2)
>         >     >> +# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.10 
> actions=conjunction(5,1/2)
>         >     >> +# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.9 
> actions=conjunction(5,1/2)
>         >     >>  
>         >     >> -OVS_WAIT_UNTIL([test 6 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >> -grep conjunction | wc -l`])
>         >     >>  OVS_WAIT_UNTIL([test 4 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >> +grep conjunction | wc -l`])
>         >     >> +OVS_WAIT_UNTIL([test 0 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >>  grep conjunction.*conjunction | wc -l`])
>         >     >>  OVS_WAIT_UNTIL([test 0 = `as hv1 ovs-ofctl dump-flows 
> br-int | \
>         >     >>  grep conjunction.*conjunction.*conjunction | wc -l`])
>         >     >> @@ -15549,11 +15553,11 @@ for i in 1 2 3; do
>         >     >>  
>         >     >>      # Update address set as1
>         >     >>      ovn-nbctl --wait=hv set addr as1 addresses="10.1.2.10 
> 10.1.2.11"
>         >     >> -    AT_CHECK([ovs-ofctl dump-flows br-int | grep 
> "10.1.2.11"], [0], [ignore])
>         >     >> +    AT_CHECK([ovs-ofctl dump-flows br-int | grep 
> "10.1.2.10/31"], [0], [ignore])
>         >     >>  
>         >     >>      # Update address set as2
>         >     >>      ovn-nbctl --wait=hv set addr as2 addresses="10.1.2.12 
> 10.1.2.13"
>         >     >> -    AT_CHECK([ovs-ofctl dump-flows br-int | grep 
> "10.1.2.12"], [0], [ignore])
>         >     >> +    AT_CHECK([ovs-ofctl dump-flows br-int | grep 
> "10.1.2.12/31"], [0], [ignore])
>         >     >>  
>         >     >>      # Add another ACL referencing as1
>         >     >>      n_flows_before=`ovs-ofctl dump-flows br-int | grep 
> "10.1.2.10" | wc -l`
>         >     >> diff --git a/tests/test-ovn.c b/tests/test-ovn.c
>         >     >> index 202a96c5d..4c13bacda 100644
>         >     >> --- a/tests/test-ovn.c
>         >     >> +++ b/tests/test-ovn.c
>         >     >> @@ -227,10 +227,19 @@ create_addr_sets(struct shash 
> *addr_sets)
>         >     >>      };
>         >     >>      static const char *const addrs4[] = { NULL };
>         >     >>  
>         >     >> +    static const char *addrs5[253];
>         >     >> +    static char addrs5_[253][20];
>         >     >> +    for (int i = 0, ip_num = 2; i < 253; i++, ip_num++) {
>         >     >> +        sprintf(addrs5_[i], "1.1.1.%d", ip_num);
>         >     >> +        addrs5[i] = addrs5_[i];
>         >     >> +    }
>         >     >> +
>         >     >>      expr_const_sets_add(addr_sets, "set1", addrs1, 3, true);
>         >     >>      expr_const_sets_add(addr_sets, "set2", addrs2, 3, true);
>         >     >>      expr_const_sets_add(addr_sets, "set3", addrs3, 3, true);
>         >     >>      expr_const_sets_add(addr_sets, "set4", addrs4, 0, true);
>         >     >> +    expr_const_sets_add(addr_sets, "set5", (const char 
> *const *)addrs5,
>         >     >> +                        253, true);
>         >     >>  }
>         >     >>  
>         >     >>  static void
>         >     >> diff --git a/utilities/automake.mk b/utilities/automake.mk
>         >     >> index c4a6d248c..af572c7a0 100644
>         >     >> --- a/utilities/automake.mk
>         >     >> +++ b/utilities/automake.mk
>         >     >> @@ -21,7 +21,8 @@ MAN_ROOTS += \
>         >     >>  bin_SCRIPTS += \
>         >     >>      utilities/ovn-docker-overlay-driver \
>         >     >>      utilities/ovn-docker-underlay-driver \
>         >     >> -    utilities/ovn-detrace
>         >     >> +    utilities/ovn-detrace \
>         >     >> +    utilities/ovn-match-ip
>         >     >>  
>         >     >>  EXTRA_DIST += \
>         >     >>      utilities/ovn-ctl \
>         >     >> @@ -35,6 +36,7 @@ EXTRA_DIST += \
>         >     >>      utilities/ovn-appctl.8.xml \
>         >     >>      utilities/ovn-trace.8.xml \
>         >     >>      utilities/ovn-detrace.in \
>         >     >> +    utilities/ovn-match-ip.in \
>         >     >>      utilities/ovndb-servers.ocf \
>         >     >>      utilities/checkpatch.py \
>         >     >>      utilities/docker/Makefile \
>         >     >> @@ -60,6 +62,7 @@ CLEANFILES += \
>         >     >>      utilities/ovn-trace.8 \
>         >     >>      utilities/ovn-detrace.1 \
>         >     >>      utilities/ovn-detrace \
>         >     >> +    utilities/ovn-match-ip \
>         >     >>      utilities/ovn-appctl.8 \
>         >     >>      utilities/ovn-appctl \
>         >     >>      utilities/ovn-sim
>         >     >> diff --git a/utilities/ovn-match-ip.in 
> b/utilities/ovn-match-ip.in
>         >     >> new file mode 100755
>         >     >> index 000000000..af8124185
>         >     >> --- /dev/null
>         >     >> +++ b/utilities/ovn-match-ip.in
>         >     >> @@ -0,0 +1,78 @@
>         >     >> +#!/bin/bash
>         >     >> +#
>         >     >> +# Licensed under the Apache License, Version 2.0 (the 
> "License");
>         >     >> +# you may not use this file except in compliance with the 
> License.
>         >     >> +# You may obtain a copy of the License at:
>         >     >> +#
>         >     >> +#     http://www.apache.org/licenses/LICENSE-2.0
>         >     >> +#
>         >     >> +# Unless required by applicable law or agreed to in 
> writing, software
>         >     >> +# distributed under the License is distributed on an "AS 
> IS" BASIS,
>         >     >> +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
> express or implied.
>         >     >> +# See the License for the specific language governing 
> permissions and
>         >     >> +# limitations under the License.
>         >     >> +
>         >     >> +IPCALC_WILDCARD_RET=
>         >     >> +
>         >     >> +ipcalc_wildcard () {
>         >     >> +    ip=`echo $1 | awk -F '/' '{print $1}'`
>         >     >> +    ip_list=${ip//./ }
>         >     >> +    read -a ip_array <<< ${ip_list}
>         >     >> +    mask=`echo $1 | awk -F '/' '{print $2}'`
>         >     >> +    mask_list=${mask//./ }
>         >     >> +    read -a mask_array <<< ${mask_list}
>         >     >> +    ip_0=$((${ip_array[0]} & ${mask_array[0]}))
>         >     >> +    ip_1=$((${ip_array[1]} & ${mask_array[1]}))
>         >     >> +    ip_2=$((${ip_array[2]} & ${mask_array[2]}))
>         >     >> +    ip_3=$((${ip_array[3]} & ${mask_array[3]}))
>         >     >> +    IPCALC_WILDCARD_RET="$ip_0.$ip_1.$ip_2.$ip_3"
>         >     >> +}
>         >     >> +
>         >     >> +for arg
>         >     >> +do
>         >     >> +    case $arg in
>         >     >> +        -h | --help)
>         >     >> +            cat <<EOF
>         >     >> +$0: A tool that can match ip wildcard mask similar to grep
>         >     >> +usage: $0 ip
>         >     >> +Search for ip/mask that can be matched in standard input.
>         >     >> +Example: echo ip 192.168.1.0/24 | $0 192.168.1.1
>         >     >> +Result: ip 192.168.1.0/24
>         >     >> +
>         >     >> +You might use this: ovs-ofctl dump-flows br-int | $0 
> 192.168.1.1
>         >     >> +EOF
>         >     >> +            exit 0
>         >     >> +            ;;
>         >     >> +    esac
>         >     >> +done
>         >     >> +
>         >     >> +while read line
>         >     >> +do
>         >     >> +    ip=`echo $line | grep -oE 
> '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+'`
>         >     >> +    if test "$ip" == "$1"; then
>         >     >> +        echo $line | grep --color $1
>         >     >> +        continue
>         >     >> +    fi
>         >     >> +
>         >     >> +    ip_mask=`echo $line | grep -oE 
> '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+'`
>         >     >> +    if test "$ip_mask" != ""; then
>         >     >> +        ipcalc_wildcard $ip_mask
>         >     >> +        ip_mask_ret=$IPCALC_WILDCARD_RET
>         >     >> +        match_ip_mask=`echo $ip_mask | sed 's/.*\//'$1'\//'`
>         >     >> +        ipcalc_wildcard $match_ip_mask
>         >     >> +        match_ip_mask_ret=$IPCALC_WILDCARD_RET
>         >     >> +        if test $ip_mask_ret == $match_ip_mask_ret; then
>         >     >> +            echo $line | grep --color $ip_mask
>         >     >> +        fi
>         >     >> +        continue
>         >     >> +    fi
>         >     >> +
>         >     >> +    ip_prefix=`echo $line | grep -oE 
> '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+'`
>         >     >> +    if test "$ip_prefix" != ""; then
>         >     >> +        match_ip_prefix=`echo $ip_prefix | sed 
> 's/.*\//'$1'\//'`
>         >     >> +        if test `ipcalc -n $match_ip_prefix` == `ipcalc -n 
> $ip_prefix`; then
>         >     >> +            echo $line | grep --color $ip_prefix
>         >     >> +        fi
>         >     >> +        continue
>         >     >> +    fi
>         >     >> +done
>         >     >>
>         >     > 
>         >     > Regards,
>         >     > Dumitru
>         >     > 
>         >     > _______________________________________________
>         >     > dev mailing list
>         >     > [email protected]
>         >     > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>         >     > 
>         > 
>         > 
>         > 
> 
> 
> 
> 

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to