LCOV - code coverage report
Current view: top level - bpfilter/cgen/matcher - ip6.c (source / functions) Coverage Total Hit
Test: coverage.lcov Lines: 97.3 % 111 108
Test Date: 2025-11-24 12:34:34 Functions: 100.0 % 5 5
Branches: 54.3 % 162 88

             Branch data     Line data    Source code
       1                 :             : /* SPDX-License-Identifier: GPL-2.0-only */
       2                 :             : /*
       3                 :             :  * Copyright (c) 2022 Meta Platforms, Inc. and affiliates.
       4                 :             :  */
       5                 :             : 
       6                 :             : #include "cgen/matcher/ip6.h"
       7                 :             : 
       8                 :             : #include <linux/bpf.h>
       9                 :             : #include <linux/bpf_common.h>
      10                 :             : #include <linux/if_ether.h>
      11                 :             : #include <linux/in.h>
      12                 :             : #include <linux/ipv6.h>
      13                 :             : 
      14                 :             : #include <endian.h>
      15                 :             : #include <errno.h>
      16                 :             : #include <stddef.h>
      17                 :             : #include <stdint.h>
      18                 :             : 
      19                 :             : #include <bpfilter/logger.h>
      20                 :             : #include <bpfilter/matcher.h>
      21                 :             : 
      22                 :             : #include "cgen/jmp.h"
      23                 :             : #include "cgen/program.h"
      24                 :             : #include "filter.h"
      25                 :             : 
      26                 :             : #define _bf_make32(a, b, c, d)                                                 \
      27                 :             :     (((uint32_t)(a) << 24) | ((uint32_t)(b) << 16) | ((uint32_t)(c) << 8) |    \
      28                 :             :      (uint32_t)(d))
      29                 :             : #define _BF_MASK_LAST_BYTE 15
      30                 :             : #define BF_IPV6_EH_HOPOPTS(x) ((x) << 0)
      31                 :             : #define BF_IPV6_EH_ROUTING(x) ((x) << 1)
      32                 :             : #define BF_IPV6_EH_FRAGMENT(x) ((x) << 2)
      33                 :             : #define BF_IPV6_EH_AH(x) ((x) << 3)
      34                 :             : #define BF_IPV6_EH_DSTOPTS(x) ((x) << 4)
      35                 :             : #define BF_IPV6_EH_MH(x) ((x) << 5)
      36                 :             : 
      37                 :          80 : static int _bf_matcher_generate_ip6_addr(struct bf_program *program,
      38                 :             :                                          const struct bf_matcher *matcher)
      39                 :             : {
      40                 :             :     struct bf_jmpctx j0, j1;
      41                 :          80 :     uint8_t *addr = (uint8_t *)bf_matcher_payload(matcher);
      42                 :          80 :     size_t offset = bf_matcher_get_type(matcher) == BF_MATCHER_IP6_SADDR ?
      43         [ +  + ]:          80 :                         offsetof(struct ipv6hdr, saddr) :
      44                 :             :                         offsetof(struct ipv6hdr, daddr);
      45                 :             : 
      46         [ -  + ]:          80 :     EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, offset));
      47         [ -  + ]:          80 :     EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, offset + 8));
      48                 :             : 
      49         [ +  + ]:          80 :     if (bf_matcher_get_op(matcher) == BF_MATCHER_EQ) {
      50                 :             :         /* If we want to match an IP, both addr[0] and addr[1]
      51                 :             :          * must match the packet, otherwise we jump to the next rule. */
      52         [ -  + ]:          40 :         EMIT(program, BPF_MOV32_IMM(BPF_REG_3, _bf_make32(addr[7], addr[6],
      53                 :             :                                                           addr[5], addr[4])));
      54         [ -  + ]:          40 :         EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
      55         [ -  + ]:          40 :         EMIT(program, BPF_MOV32_IMM(BPF_REG_4, _bf_make32(addr[3], addr[2],
      56                 :             :                                                           addr[1], addr[0])));
      57         [ -  + ]:          40 :         EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
      58         [ -  + ]:          40 :         EMIT_FIXUP_JMP_NEXT_RULE(program,
      59                 :             :                                  BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 0));
      60                 :             : 
      61         [ -  + ]:          40 :         EMIT(program, BPF_MOV32_IMM(BPF_REG_3, _bf_make32(addr[15], addr[14],
      62                 :             :                                                           addr[13], addr[12])));
      63         [ -  + ]:          40 :         EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
      64         [ -  + ]:          40 :         EMIT(program, BPF_MOV32_IMM(BPF_REG_4, _bf_make32(addr[11], addr[10],
      65                 :             :                                                           addr[9], addr[8])));
      66         [ -  + ]:          40 :         EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
      67         [ -  + ]:          40 :         EMIT_FIXUP_JMP_NEXT_RULE(program,
      68                 :             :                                  BPF_JMP_REG(BPF_JNE, BPF_REG_2, BPF_REG_3, 0));
      69                 :             :     } else {
      70                 :             :         /* If we want to *not* match an IP, none of addr[0] and
      71                 :             :          * addr[1] should match the packet, otherwise we jump to the
      72                 :             :          * next rule. */
      73         [ -  + ]:          40 :         EMIT(program, BPF_MOV32_IMM(BPF_REG_3, _bf_make32(addr[7], addr[6],
      74                 :             :                                                           addr[5], addr[4])));
      75         [ -  + ]:          40 :         EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
      76         [ -  + ]:          40 :         EMIT(program, BPF_MOV32_IMM(BPF_REG_4, _bf_make32(addr[3], addr[2],
      77                 :             :                                                           addr[1], addr[0])));
      78         [ -  + ]:          40 :         EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
      79                 :             : 
      80                 :             :         /* Branching:
      81                 :             :          * - addr[0] matches the address' 64 MSB: continue to compare
      82                 :             :          *   the address' 64 LSB.
      83                 :             :          * - addr[0] doesn't matches the address' 64 MSB: jump to the
      84                 :             :          *   end of the matcher to continue the processing of the current rule.
      85                 :             :          *   This matcher matched. */
      86         [ -  + ]:          40 :         j0 = bf_jmpctx_get(program,
      87                 :             :                            BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 0));
      88                 :             : 
      89         [ -  + ]:          40 :         EMIT(program, BPF_MOV32_IMM(BPF_REG_3, _bf_make32(addr[15], addr[14],
      90                 :             :                                                           addr[13], addr[12])));
      91         [ -  + ]:          40 :         EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
      92         [ -  + ]:          40 :         EMIT(program, BPF_MOV32_IMM(BPF_REG_4, _bf_make32(addr[11], addr[10],
      93                 :             :                                                           addr[9], addr[8])));
      94         [ -  + ]:          40 :         EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
      95                 :             : 
      96                 :             :         /* Branching:
      97                 :             :          * - addr[1] matches the address' 64 LSB: addr matches the
      98                 :             :          *   packet's address, meaning the matcher doesn't match. Jump to the
      99                 :             :          *   next rule.
     100                 :             :          * - addr[1] doesn't matches the address' 64 LSB: the matcher
     101                 :             :          *   matched: addr is not equal to the packet's address. Continue
     102                 :             :          *   processing with the next matcher. */
     103         [ -  + ]:          40 :         j1 = bf_jmpctx_get(program,
     104                 :             :                            BPF_JMP_REG(BPF_JNE, BPF_REG_2, BPF_REG_3, 0));
     105                 :             : 
     106         [ -  + ]:          40 :         EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP_A(0));
     107                 :             : 
     108                 :             :         // j0 and j1 should jump here if they can't match the packet's IP.
     109                 :          40 :         bf_jmpctx_cleanup(&j0);
     110                 :          40 :         bf_jmpctx_cleanup(&j1);
     111                 :             :     }
     112                 :             : 
     113                 :             :     return 0;
     114                 :             : }
     115                 :             : 
     116                 :          90 : static void _bf_ip6_prefix_to_mask(uint32_t prefixlen, uint8_t *mask)
     117                 :             : {
     118                 :             :     bf_assert(mask);
     119                 :             : 
     120                 :          90 :     memset(mask, 0x00, 16);
     121                 :             : 
     122                 :          90 :     memset(mask, 0xff, prefixlen / 8);
     123         [ +  + ]:          90 :     if (prefixlen % 8)
     124                 :          10 :         mask[prefixlen / 8] = 0xff << (8 - prefixlen % 8) & 0xff;
     125                 :          90 : }
     126                 :             : 
     127                 :          90 : static int _bf_matcher_generate_ip6_net(struct bf_program *program,
     128                 :             :                                         const struct bf_matcher *matcher)
     129                 :             : {
     130                 :             :     uint8_t mask[16];
     131                 :             :     struct bf_jmpctx j0, j1;
     132                 :          90 :     const struct bf_ip6_lpm_key *addr = bf_matcher_payload(matcher);
     133                 :          90 :     size_t offset = bf_matcher_get_type(matcher) == BF_MATCHER_IP6_SNET ?
     134         [ +  + ]:          90 :                         offsetof(struct ipv6hdr, saddr) :
     135                 :             :                         offsetof(struct ipv6hdr, daddr);
     136                 :             : 
     137         [ -  + ]:          90 :     EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, offset));
     138         [ -  + ]:          90 :     EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, offset + 8));
     139                 :             : 
     140                 :          90 :     _bf_ip6_prefix_to_mask(addr->prefixlen, mask);
     141                 :             : 
     142         [ +  + ]:          90 :     if (mask[_BF_MASK_LAST_BYTE] != (uint8_t)~0) {
     143         [ -  + ]:          50 :         EMIT(program, BPF_MOV32_IMM(BPF_REG_3, _bf_make32(mask[7], mask[6],
     144                 :             :                                                           mask[5], mask[4])));
     145         [ -  + ]:          50 :         EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
     146         [ -  + ]:          50 :         EMIT(program, BPF_MOV32_IMM(BPF_REG_4, _bf_make32(mask[3], mask[2],
     147                 :             :                                                           mask[1], mask[0])));
     148         [ -  + ]:          50 :         EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
     149         [ -  + ]:          50 :         EMIT(program, BPF_ALU64_REG(BPF_AND, BPF_REG_1, BPF_REG_3));
     150                 :             : 
     151         [ -  + ]:          50 :         EMIT(program, BPF_MOV32_IMM(BPF_REG_3, _bf_make32(mask[15], mask[14],
     152                 :             :                                                           mask[13], mask[12])));
     153         [ -  + ]:          50 :         EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
     154         [ -  + ]:          50 :         EMIT(program, BPF_MOV32_IMM(BPF_REG_4, _bf_make32(mask[11], mask[10],
     155                 :             :                                                           mask[9], mask[8])));
     156         [ -  + ]:          50 :         EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
     157         [ -  + ]:          50 :         EMIT(program, BPF_ALU64_REG(BPF_AND, BPF_REG_2, BPF_REG_3));
     158                 :             :     }
     159                 :             : 
     160         [ +  + ]:          90 :     if (bf_matcher_get_op(matcher) == BF_MATCHER_EQ) {
     161                 :             :         /* If we want to match an IP, both addr->data[0] and addr->data[1]
     162                 :             :          * must match the packet, otherwise we jump to the next rule. */
     163         [ -  + ]:          50 :         EMIT(program,
     164                 :             :              BPF_MOV32_IMM(BPF_REG_3, _bf_make32(addr->data[7] & mask[7],
     165                 :             :                                                  addr->data[6] & mask[6],
     166                 :             :                                                  addr->data[5] & mask[5],
     167                 :             :                                                  addr->data[4] & mask[4])));
     168         [ -  + ]:          50 :         EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
     169         [ -  + ]:          50 :         EMIT(program,
     170                 :             :              BPF_MOV32_IMM(BPF_REG_4, _bf_make32(addr->data[3] & mask[3],
     171                 :             :                                                  addr->data[2] & mask[2],
     172                 :             :                                                  addr->data[1] & mask[1],
     173                 :             :                                                  addr->data[0] & mask[0])));
     174         [ -  + ]:          50 :         EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
     175         [ -  + ]:          50 :         EMIT_FIXUP_JMP_NEXT_RULE(program,
     176                 :             :                                  BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 0));
     177                 :             : 
     178         [ -  + ]:          50 :         EMIT(program,
     179                 :             :              BPF_MOV32_IMM(BPF_REG_3, _bf_make32(addr->data[15] & mask[15],
     180                 :             :                                                  addr->data[14] & mask[14],
     181                 :             :                                                  addr->data[13] & mask[13],
     182                 :             :                                                  addr->data[12] & mask[12])));
     183         [ -  + ]:          50 :         EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
     184         [ -  + ]:          50 :         EMIT(program,
     185                 :             :              BPF_MOV32_IMM(BPF_REG_4, _bf_make32(addr->data[11] & mask[11],
     186                 :             :                                                  addr->data[10] & mask[10],
     187                 :             :                                                  addr->data[9] & mask[9],
     188                 :             :                                                  addr->data[8] & mask[8])));
     189         [ -  + ]:          50 :         EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
     190         [ -  + ]:          50 :         EMIT_FIXUP_JMP_NEXT_RULE(program,
     191                 :             :                                  BPF_JMP_REG(BPF_JNE, BPF_REG_2, BPF_REG_3, 0));
     192                 :             :     } else {
     193                 :             :         /* If we want to *not* match an IP, none of addr->data[0] and
     194                 :             :          * addr->data[1] should match the packet, otherwise we jump to the
     195                 :             :          * next rule. */
     196         [ -  + ]:          40 :         EMIT(program,
     197                 :             :              BPF_MOV32_IMM(BPF_REG_3, _bf_make32(addr->data[7] & mask[7],
     198                 :             :                                                  addr->data[6] & mask[6],
     199                 :             :                                                  addr->data[5] & mask[5],
     200                 :             :                                                  addr->data[4] & mask[4])));
     201         [ -  + ]:          40 :         EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
     202         [ -  + ]:          40 :         EMIT(program,
     203                 :             :              BPF_MOV32_IMM(BPF_REG_4, _bf_make32(addr->data[3] & mask[3],
     204                 :             :                                                  addr->data[2] & mask[2],
     205                 :             :                                                  addr->data[1] & mask[1],
     206                 :             :                                                  addr->data[0] & mask[0])));
     207         [ -  + ]:          40 :         EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
     208                 :             : 
     209                 :             :         /* Branching:
     210                 :             :          * - addr->data[0] matches the address' 64 MSB: continue to compare
     211                 :             :          *   the address' 64 LSB.
     212                 :             :          * - addr->data[0] doesn't matches the address' 64 MSB: jump to the
     213                 :             :          *   end of the matcher to continue the processing of the current rule.
     214                 :             :          *   This matcher matched. */
     215         [ -  + ]:          40 :         j0 = bf_jmpctx_get(program,
     216                 :             :                            BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 0));
     217                 :             : 
     218         [ -  + ]:          40 :         EMIT(program,
     219                 :             :              BPF_MOV32_IMM(BPF_REG_3, _bf_make32(addr->data[15] & mask[15],
     220                 :             :                                                  addr->data[14] & mask[14],
     221                 :             :                                                  addr->data[13] & mask[13],
     222                 :             :                                                  addr->data[12] & mask[12])));
     223         [ -  + ]:          40 :         EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32));
     224         [ -  + ]:          40 :         EMIT(program,
     225                 :             :              BPF_MOV32_IMM(BPF_REG_4, _bf_make32(addr->data[11] & mask[11],
     226                 :             :                                                  addr->data[10] & mask[10],
     227                 :             :                                                  addr->data[9] & mask[9],
     228                 :             :                                                  addr->data[8] & mask[8])));
     229         [ -  + ]:          40 :         EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_3, BPF_REG_4));
     230                 :             : 
     231                 :             :         /* Branching:
     232                 :             :          * - addr->data[1] matches the address' 64 LSB: addr->data matches the
     233                 :             :          *   packet's address, meaning the matcher doesn't match. Jump to the
     234                 :             :          *   next rule.
     235                 :             :          * - addr->data[1] doesn't matches the address' 64 LSB: the matcher
     236                 :             :          *   matched: addr->data is not equal to the packet's address. Continue
     237                 :             :          *   processing with the next matcher. */
     238         [ -  + ]:          40 :         j1 = bf_jmpctx_get(program,
     239                 :             :                            BPF_JMP_REG(BPF_JNE, BPF_REG_2, BPF_REG_3, 0));
     240                 :             : 
     241         [ -  + ]:          40 :         EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP_A(0));
     242                 :             : 
     243                 :             :         // j0 and j1 should jump here if they can't match the packet's IP.
     244                 :          40 :         bf_jmpctx_cleanup(&j0);
     245                 :          40 :         bf_jmpctx_cleanup(&j1);
     246                 :             :     }
     247                 :             : 
     248                 :             :     return 0;
     249                 :             : }
     250                 :             : 
     251                 :          80 : static int _bf_matcher_generate_ip6_nexthdr(struct bf_program *program,
     252                 :             :                                             const struct bf_matcher *matcher)
     253                 :             : {
     254                 :          80 :     const uint8_t ehdr = *(uint8_t *)bf_matcher_payload(matcher);
     255                 :             :     uint8_t eh_mask;
     256                 :             : 
     257   [ -  +  -  - ]:          80 :     if ((bf_matcher_get_op(matcher) != BF_MATCHER_EQ) &&
     258                 :           0 :         (bf_matcher_get_op(matcher) != BF_MATCHER_NE))
     259                 :             :         return -EINVAL;
     260                 :             : 
     261         [ +  + ]:          80 :     switch (ehdr) {
     262                 :          20 :     case IPPROTO_HOPOPTS:
     263                 :             :     case IPPROTO_ROUTING:
     264                 :             :     case IPPROTO_DSTOPTS:
     265                 :             :     case IPPROTO_FRAGMENT:
     266                 :             :     case IPPROTO_AH:
     267                 :             :     case IPPROTO_MH:
     268         [ +  + ]:          20 :         eh_mask = (BF_IPV6_EH_HOPOPTS(ehdr == IPPROTO_HOPOPTS) |
     269         [ +  - ]:          10 :                    BF_IPV6_EH_ROUTING(ehdr == IPPROTO_ROUTING) |
     270         [ -  + ]:          20 :                    BF_IPV6_EH_FRAGMENT(ehdr == IPPROTO_FRAGMENT) |
     271         [ -  + ]:          20 :                    BF_IPV6_EH_AH(ehdr == IPPROTO_AH) |
     272         [ +  - ]:          20 :                    BF_IPV6_EH_DSTOPTS(ehdr == IPPROTO_DSTOPTS) |
     273                 :             :                    BF_IPV6_EH_MH(ehdr == IPPROTO_MH));
     274         [ -  + ]:          20 :         EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10,
     275                 :             :                                   BF_PROG_CTX_OFF(ipv6_eh)));
     276         [ -  + ]:          20 :         EMIT(program, BPF_ALU64_IMM(BPF_AND, BPF_REG_1, eh_mask));
     277   [ -  +  -  + ]:          20 :         EMIT_FIXUP_JMP_NEXT_RULE(
     278                 :             :             program, BPF_JMP_IMM((bf_matcher_get_op(matcher) == BF_MATCHER_EQ) ?
     279                 :             :                                      BPF_JEQ :
     280                 :             :                                      BPF_JNE,
     281                 :             :                                  BPF_REG_1, 0, 0));
     282                 :          20 :         break;
     283                 :          60 :     default:
     284                 :             :         /* check l4 protocols using BPF_REG_8 */
     285   [ -  +  -  + ]:          60 :         EMIT_FIXUP_JMP_NEXT_RULE(
     286                 :             :             program, BPF_JMP_IMM((bf_matcher_get_op(matcher) == BF_MATCHER_EQ) ?
     287                 :             :                                      BPF_JNE :
     288                 :             :                                      BPF_JEQ,
     289                 :             :                                  BPF_REG_8, ehdr, 0));
     290                 :          60 :         break;
     291                 :             :     }
     292                 :             : 
     293                 :             :     return 0;
     294                 :             : }
     295                 :             : 
     296                 :         250 : int bf_matcher_generate_ip6(struct bf_program *program,
     297                 :             :                             const struct bf_matcher *matcher)
     298                 :             : {
     299                 :             :     int r;
     300                 :             : 
     301         [ -  + ]:         250 :     EMIT_FIXUP_JMP_NEXT_RULE(
     302                 :             :         program, BPF_JMP_IMM(BPF_JNE, BPF_REG_7, htobe16(ETH_P_IPV6), 0));
     303                 :             : 
     304         [ -  + ]:         250 :     EMIT(program,
     305                 :             :          BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_10, BF_PROG_CTX_OFF(l3_hdr)));
     306                 :             : 
     307   [ +  +  +  - ]:         250 :     switch (bf_matcher_get_type(matcher)) {
     308                 :          80 :     case BF_MATCHER_IP6_SADDR:
     309                 :             :     case BF_MATCHER_IP6_DADDR:
     310                 :          80 :         r = _bf_matcher_generate_ip6_addr(program, matcher);
     311                 :          80 :         break;
     312                 :          90 :     case BF_MATCHER_IP6_SNET:
     313                 :             :     case BF_MATCHER_IP6_DNET:
     314                 :          90 :         r = _bf_matcher_generate_ip6_net(program, matcher);
     315                 :          90 :         break;
     316                 :          80 :     case BF_MATCHER_IP6_NEXTHDR:
     317                 :          80 :         r = _bf_matcher_generate_ip6_nexthdr(program, matcher);
     318                 :          80 :         break;
     319                 :           0 :     default:
     320         [ #  # ]:           0 :         return bf_err_r(-EINVAL, "unknown matcher type %d",
     321                 :             :                         bf_matcher_get_type(matcher));
     322                 :             :     };
     323                 :             : 
     324                 :             :     return r;
     325                 :             : }
        

Generated by: LCOV version 2.0-1