On Linux compiling a filter that includes "inbound", "outbound" or
"ifindex" for a pcap_t that was created using pcap_open_dead() produces
bytecode that uses BPF extensions if the DLT is not one of the known
DLTs:
$ filtertest RAW '(inbound and ifindex 1) or (outbound and ifindex 2)'
(000) ldh [type]
(001) jeq #0x4 jt 2 jf 4
(002) ld [ifidx]
(003) jeq #0x2 jt 6 jf 7
(004) ld [ifidx]
(005) jeq #0x1 jt 6 jf 7
(006) ret #262144
(007) ret #0
This is because both gen_ifindex() and gen_inbound_outbound() assume
that a live capture is equivalent to rfile == NULL, which is not true.
Fix this by using the logic that already works correctly for the "vlan"
keyword. Define a new flag for bpf_codegen_flags and set it in
setup_socket() (which is in the code path for a live capture only) if
any BPF extensions are available. Check for the flag in a new helper
function and use it instead of the incorrect rfile tests. Set the flag
in filtertest as well. Now pcap_compile() handles both cases correctly
on Linux:
$ filtertest RAW 'ifindex 1'
filtertest: ifindex not supported on Raw IP (not a live capture)
$ filtertest -l RAW 'ifindex 1'
(000) ld [ifidx]
(001) jeq #0x1 jt 2 jf 3
(002) ret #262144
(003) ret #0
Make two existing accept tests for "inbound" and "outbound" specific to
the extensions and add a similar test for "ifindex". Add more reject
tests to cover the three keywords on Linux w/o extensions and non-Linux
OSes separately.
Require "[wlan] dir" integer value to be within range.
Fix the != comparison for ATM and MTP field values.
Deprecate bpf_filter().
Require "[wlan] dir" integer value to be within range.
Fix the != comparison for ATM and MTP field values.
Deprecate bpf_filter().
+ Require a live capture for all Linux BPF extensions.
rpcap:
Support user names and passwords in rpcap:// and rpcaps:// URLs.
Add a -t flag to rpcapd to specify the data channel port; from
rpcap:
Support user names and passwords in rpcap:// and rpcaps:// URLs.
Add a -t flag to rpcapd to specify the data channel port; from
+#ifdef __linux__
+/*
+ * This is Linux; we require PF_PACKET support. If this is a *live* capture,
+ * we can look at special meta-data in the filter expression; otherwise we
+ * can't because it is either a savefile (rfile != NULL) or a pcap_t created
+ * using pcap_open_dead() (rfile == NULL). Thus check for a flag that
+ * pcap_activate() conditionally sets.
+ */
+static void
+require_basic_bpf_extensions(compiler_state_t *cstate, const char *keyword)
+{
+ if (cstate->bpf_pcap->bpf_codegen_flags & BPF_SPECIAL_BASIC_HANDLING)
+ return;
+ bpf_error(cstate, "%s not supported on %s (not a live capture)",
+ keyword,
+ pcap_datalink_val_to_description_or_dlt(cstate->linktype));
+}
+#endif // __linux__
+
struct block *
gen_ifindex(compiler_state_t *cstate, int ifindex)
{
struct block *
gen_ifindex(compiler_state_t *cstate, int ifindex)
{
break;
default:
#if defined(__linux__)
break;
default:
#if defined(__linux__)
- /*
- * This is Linux; we require PF_PACKET support.
- * If this is a *live* capture, we can look at
- * special meta-data in the filter expression;
- * if it's a savefile, we can't.
- */
- if (cstate->bpf_pcap->rfile != NULL) {
- /* We have a FILE *, so this is a savefile */
- bpf_error(cstate, "ifindex not supported on %s when reading savefiles",
- pcap_datalink_val_to_description_or_dlt(cstate->linktype));
- /*NOTREACHED*/
- }
+ require_basic_bpf_extensions(cstate, "ifindex");
/* match ifindex */
b0 = gen_cmp(cstate, OR_LINKHDR, SKF_AD_OFF + SKF_AD_IFINDEX, BPF_W,
ifindex);
/* match ifindex */
b0 = gen_cmp(cstate, OR_LINKHDR, SKF_AD_OFF + SKF_AD_IFINDEX, BPF_W,
ifindex);
* in pcapng files.
*/
#if defined(__linux__)
* in pcapng files.
*/
#if defined(__linux__)
- /*
- * This is Linux; we require PF_PACKET support.
- * If this is a *live* capture, we can look at
- * special meta-data in the filter expression;
- * if it's a savefile, we can't.
- */
- if (cstate->bpf_pcap->rfile != NULL) {
- /* We have a FILE *, so this is a savefile */
- bpf_error(cstate, "inbound/outbound not supported on %s when reading savefiles",
- pcap_datalink_val_to_description_or_dlt(cstate->linktype));
- /*NOTREACHED*/
- }
+ require_basic_bpf_extensions(cstate, outbound ? "outbound" : "inbound");
/* match outgoing packets */
b0 = gen_cmp(cstate, OR_LINKHDR, SKF_AD_OFF + SKF_AD_PKTTYPE, BPF_H,
PACKET_OUTGOING);
/* match outgoing packets */
b0 = gen_cmp(cstate, OR_LINKHDR, SKF_AD_OFF + SKF_AD_PKTTYPE, BPF_H,
PACKET_OUTGOING);
* BPF code generation flags.
*/
#define BPF_SPECIAL_VLAN_HANDLING 0x00000001 /* special VLAN handling for Linux */
* BPF code generation flags.
*/
#define BPF_SPECIAL_VLAN_HANDLING 0x00000001 /* special VLAN handling for Linux */
+/*
+ * Special handling of packet type and ifindex, which are some of the auxiliary
+ * data items available in Linux >= 2.6.27. Disregard protocol and netlink
+ * attributes for now.
+ */
+#define BPF_SPECIAL_BASIC_HANDLING 0x00000002
/*
* User data structure for the one-shot callback used for pcap_next()
/*
* User data structure for the one-shot callback used for pcap_next()
-#if defined(SO_BPF_EXTENSIONS) && defined(SKF_AD_VLAN_TAG_PRESENT)
+#ifdef SO_BPF_EXTENSIONS
/*
* Can we generate special code for VLAN checks?
* (XXX - what if we need the special code but it's not supported
/*
* Can we generate special code for VLAN checks?
* (XXX - what if we need the special code but it's not supported
*/
if (getsockopt(sock_fd, SOL_SOCKET, SO_BPF_EXTENSIONS,
&bpf_extensions, &len) == 0) {
*/
if (getsockopt(sock_fd, SOL_SOCKET, SO_BPF_EXTENSIONS,
&bpf_extensions, &len) == 0) {
+ /*
+ * This is a live capture with some BPF extensions support,
+ * so indicate that at least the auxiliary data items from
+ * Linux 2.6.27 are available (this concerns SKF_AD_PKTTYPE
+ * and SKF_AD_IFINDEX in the first place).
+ */
+ handle->bpf_codegen_flags |= BPF_SPECIAL_BASIC_HANDLING;
+#ifdef SKF_AD_VLAN_TAG_PRESENT
if (bpf_extensions >= SKF_AD_VLAN_TAG_PRESENT) {
/*
* Yes, we can. Request that we do so.
*/
handle->bpf_codegen_flags |= BPF_SPECIAL_VLAN_HANDLING;
}
if (bpf_extensions >= SKF_AD_VLAN_TAG_PRESENT) {
/*
* Yes, we can. Request that we do so.
*/
handle->bpf_codegen_flags |= BPF_SPECIAL_VLAN_HANDLING;
}
+#endif // SKF_AD_VLAN_TAG_PRESENT
-#endif /* defined(SO_BPF_EXTENSIONS) && defined(SKF_AD_VLAN_TAG_PRESENT) */
+#endif // SO_BPF_EXTENSIONS
(004) ret #0
EOF
}, # juniper_mfr_outbound
(004) ret #0
EOF
}, # juniper_mfr_outbound
- # The two tests below represent the current Linux-specific implementation,
- # which is not consistent in which function -- pcap_compile() or
- # pcap_setfilter() -- and under which conditions patches the bytecode for
- # specific keywords. So this behaviour may change in future.
- linux_skf_ad_inbound => {
DLT => 'EN10MB',
expr => 'inbound',
unopt => <<~'EOF',
DLT => 'EN10MB',
expr => 'inbound',
unopt => <<~'EOF',
(002) ret #0
(003) ret #262144
EOF
(002) ret #0
(003) ret #262144
EOF
- }, # linux_skf_ad_inbound
- linux_skf_ad_outbound => {
+ }, # inbound_linuxext
+ outbound_linuxext => {
DLT => 'EN10MB',
expr => 'outbound',
unopt => <<~'EOF',
DLT => 'EN10MB',
expr => 'outbound',
unopt => <<~'EOF',
(002) ret #262144
(003) ret #0
EOF
(002) ret #262144
(003) ret #0
EOF
- }, # linux_skf_ad_inbound
+ }, # outbound_linuxext
+ ifindex_linuxext => {
+ skip => is_not_linux(),
+ linuxext => 1,
+ DLT => 'EN10MB',
+ expr => 'ifindex 10',
+ unopt => <<~'EOF',
+ (000) ld [ifidx]
+ (001) jeq #0xa jt 2 jf 3
+ (002) ret #262144
+ (003) ret #0
+ EOF
+ }, # ifindex_linuxext
mtp2_fisu => {
DLT => 'MTP2',
mtp2_fisu => {
DLT => 'MTP2',
expr => 'mpls 1048576',
errstr => 'greater than maximum',
},
expr => 'mpls 1048576',
errstr => 'greater than maximum',
},
- inbound_not_supported => {
+ inbound_not_supported_linux => {
+ skip => is_not_linux(),
+ DLT => 'EN10MB',
+ expr => 'inbound',
+ errstr => 'not a live capture',
+ },
+ outbound_not_supported_linux => {
+ skip => is_not_linux(),
+ DLT => 'EN10MB',
+ expr => 'outbound',
+ errstr => 'not a live capture',
+ },
+ ifindex_not_supported_linux => {
+ skip => is_not_linux(),
+ DLT => 'LINUX_SLL',
+ expr => 'ifindex 1',
+ errstr => 'not a live capture',
+ },
+ inbound_not_supported_other => {
skip => is_linux(),
DLT => 'EN10MB',
expr => 'inbound',
skip => is_linux(),
DLT => 'EN10MB',
expr => 'inbound',
- errstr => 'inbound/outbound not supported',
+ errstr => 'not supported',
- outbound_not_supported => {
+ outbound_not_supported_other => {
skip => is_linux(),
DLT => 'EN10MB',
expr => 'outbound',
skip => is_linux(),
DLT => 'EN10MB',
expr => 'outbound',
- errstr => 'inbound/outbound not supported',
+ errstr => 'not supported',
+ },
+ ifindex_not_supported_other => {
+ skip => is_linux(),
+ DLT => 'EN10MB',
+ expr => 'ifindex 1',
+ errstr => 'not supported',
#ifdef LINUX_BPF_EXT
if (lflag) {
pd->bpf_codegen_flags |= BPF_SPECIAL_VLAN_HANDLING;
#ifdef LINUX_BPF_EXT
if (lflag) {
pd->bpf_codegen_flags |= BPF_SPECIAL_VLAN_HANDLING;
+ pd->bpf_codegen_flags |= BPF_SPECIAL_BASIC_HANDLING;