default:
                        return -EINVAL;
                }
+       case BPF_PROG_TYPE_NETFILTER:
+               if (expected_attach_type == BPF_NETFILTER)
+                       return 0;
+               return -EINVAL;
        case BPF_PROG_TYPE_SYSCALL:
        case BPF_PROG_TYPE_EXT:
                if (expected_attach_type)
 
        switch (prog->type) {
        case BPF_PROG_TYPE_EXT:
+               break;
        case BPF_PROG_TYPE_NETFILTER:
+               if (attr->link_create.attach_type != BPF_NETFILTER) {
+                       ret = -EINVAL;
+                       goto out;
+               }
                break;
        case BPF_PROG_TYPE_PERF_EVENT:
        case BPF_PROG_TYPE_TRACEPOINT:
 
        [BPF_PERF_EVENT]                = "perf_event",
        [BPF_TRACE_KPROBE_MULTI]        = "trace_kprobe_multi",
        [BPF_STRUCT_OPS]                = "struct_ops",
+       [BPF_NETFILTER]                 = "netfilter",
 };
 
 static const char * const link_type_name[] = {
        SEC_DEF("struct_ops+",          STRUCT_OPS, 0, SEC_NONE),
        SEC_DEF("struct_ops.s+",        STRUCT_OPS, 0, SEC_SLEEPABLE),
        SEC_DEF("sk_lookup",            SK_LOOKUP, BPF_SK_LOOKUP, SEC_ATTACHABLE),
-       SEC_DEF("netfilter",            NETFILTER, 0, SEC_NONE),
+       SEC_DEF("netfilter",            NETFILTER, BPF_NETFILTER, SEC_NONE),
 };
 
 static size_t custom_sec_def_cnt;
 
        case BPF_PROG_TYPE_SK_REUSEPORT:
        case BPF_PROG_TYPE_FLOW_DISSECTOR:
        case BPF_PROG_TYPE_CGROUP_SYSCTL:
+               break;
        case BPF_PROG_TYPE_NETFILTER:
+               opts.expected_attach_type = BPF_NETFILTER;
                break;
        default:
                return -EOPNOTSUPP;