Browse Source

Add support for EMM filters similar to DVBAPI filters (match + mask).

Georgi Chorbadzhiyski 11 years ago
parent
commit
dc4df6e288
4 changed files with 207 additions and 74 deletions
  1. 1
    0
      ChangeLog
  2. 52
    17
      FILTERING
  3. 11
    4
      data.h
  4. 143
    53
      filter.c

+ 1
- 0
ChangeLog View File

8
    working if the output address was not multicast.
8
    working if the output address was not multicast.
9
  * Add support for Griffin CAS and for DGCrypt CAS.
9
  * Add support for Griffin CAS and for DGCrypt CAS.
10
  * Add support for setting multicast source address /SSM/ (--input-source).
10
  * Add support for setting multicast source address /SSM/ (--input-source).
11
+ * Add support for EMM filters similar to DVBAPI filters (match + mask).
11
 
12
 
12
 2012-09-10 : Version 9.0
13
 2012-09-10 : Version 9.0
13
  * Add --no-output-on-error (-u) option. By using this option output can be
14
  * Add --no-output-on-error (-u) option. By using this option output can be

+ 52
- 17
FILTERING View File

67
  * name=X        - Sets the filter name (used when displaying filters).
67
  * name=X        - Sets the filter name (used when displaying filters).
68
                    The name can not be longer than 32 symbols.
68
                    The name can not be longer than 32 symbols.
69
 
69
 
70
+ * match=X1 X4 X5
71
+ * mask=M1 M4 M5
72
+                 - Match bytes are series of hex numbers separated by
73
+                   space " " or dot ".". When the bytes are processed 0x
74
+                   prefix is removed from them. The maximum match bytes
75
+                   is 16.
76
+
77
+                   The match bytes are compared to the incoming packet
78
+                   by first applying the mask (binary AND operation) set
79
+                   in mask= setting. The default mask is 0xFF.
80
+
81
+                   The match bytes are compared to first, forth and so
82
+                   on bytes in the incoming EMM packet. Second and third
83
+                   incoming bytes in the EMM are not checked (they specify
84
+                   the section length).
85
+
86
+                   Here is an example EMM and match+mask that would match:
87
+
88
+                      Pos:  1  2  3  4  5  6  7  8  9 10 11 12 13
89
+                           -- -- -- -- -- -- -- -- -- -- -- -- --
90
+                      EMM: 82 70 b4 aa bb cc d0 00 01 xx xx xx xx
91
+                    Match: 82       aa bb cc dd
92
+                     Mask: ff       ff ff ff f0
93
+
94
+                   Such filter is configured like that:
95
+                     --emm-filter "accept/name=Test_Filter,match=82 aa bb cc dd,mask=ff ff ff ff f0"
96
+
70
  * offset=X      - Sets the offset at which data bytes would be checked
97
  * offset=X      - Sets the offset at which data bytes would be checked
71
                    against EMM. The default offset is 0.
98
                    against EMM. The default offset is 0.
72
 
99
 
100
+                   This setting is ignored when match+mask are set.
101
+
73
  * data=XX YY ZZ - Data bytes are series of hex numbers separated by
102
  * data=XX YY ZZ - Data bytes are series of hex numbers separated by
74
                    space " " or dot ".". When data bytes are processed 0x
103
                    space " " or dot ".". When data bytes are processed 0x
75
                    prefix is removed from them. The maximum data bytes is 16.
104
                    prefix is removed from them. The maximum data bytes is 16.
76
 
105
 
106
+                   This setting is ignored when match+mask is set.
107
+
108
+                   Using offset+data you can check any bytes in the
109
+                   incoming EMM packet.
110
+
77
 Filter processing
111
 Filter processing
78
 =================
112
 =================
79
 Filters are processed one by one until "accept" or "reject" filter matches.
113
 Filters are processed one by one until "accept" or "reject" filter matches.
83
 Example filters
117
 Example filters
84
 ===============
118
 ===============
85
 
119
 
120
+    Accept Bulcrypt EMMs that are for particular card (or card group). The card
121
+    hex serial number is "aa bb cc dd".
122
+
123
+       tsdecrypt ... \
124
+         --emm \
125
+         --emm-filter reject_all \
126
+         --emm-filter "accept/name=Bulcrypt_EMMs,match=82 aa bb cc dd,mask=ff ff ff ff f0" \
127
+         --emm-filter "accept/name=Bulcrypt_EMMs,match=84 aa bb cc dd,mask=ff ff ff ff f0" \
128
+         --emm-filter "accept/name=Bulcrypt_EMMs,match=85 aa bb cc dd,mask=ff ff ff ff f0"
129
+
86
   Bulcrypt white list example:
130
   Bulcrypt white list example:
87
     Accept all EMMs except those starting with 8a 70 b4, 8b 70 b4, 8c or 8d.
131
     Accept all EMMs except those starting with 8a 70 b4, 8b 70 b4, 8c or 8d.
88
 
132
 
89
        tsdecrypt ... \
133
        tsdecrypt ... \
134
+         --emm \
90
          --emm-filter accept_all \
135
          --emm-filter accept_all \
91
          --emm-filter "reject/name=Bulcrypt_unknown_EMMs,offset=0,data=8a 70 b4" \
136
          --emm-filter "reject/name=Bulcrypt_unknown_EMMs,offset=0,data=8a 70 b4" \
92
          --emm-filter "reject/name=Bulcrypt_unknown_EMMs,offset=0,data=8b 70 b4" \
137
          --emm-filter "reject/name=Bulcrypt_unknown_EMMs,offset=0,data=8b 70 b4" \
93
          --emm-filter "reject/name=Some EMM,data=0x8c" \
138
          --emm-filter "reject/name=Some EMM,data=0x8c" \
94
-         --emm-filter reject/data=0x8d \
139
+         --emm-filter "reject/data=0x8d"
95
 
140
 
96
   Bulcrypt black list example:
141
   Bulcrypt black list example:
97
     Reject all EMMs that don't start with 82, 84 or 85.
142
     Reject all EMMs that don't start with 82, 84 or 85.
98
 
143
 
99
-      tsdecrypt ... \
100
-        --emm-filter reject_all \
101
-        --emm-filter accept/name=Bulcrypt_EMMs,offset=0,data=82 \
102
-        --emm-filter accept/name=Bulcrypt_EMMs,data=84 \
103
-        --emm-filter accept/name=Bulcrypt_EMMs,data=85 \
104
-
105
-  Example combination that builds white/black list.
106
-    Accept EMMs that start with 8a and 8b also accept EMMs which have
107
-    70 b4 at offset 1, and reject everything else.
108
-
109
-      tsdecrypt ... \
110
-        --emm-filter accept_all \
111
-        --emm-filter reject/name=Bulcrypt_unknown_EMMs,data=8a \
112
-        --emm-filter reject/name=Bulcrypt_unknown_EMMs,data=8b \
113
-        --emm-filter reject_all \
114
-        --emm-filter "accept/name=Bulcrypt_EMMs_with_correct_size,offset=1,data=70 b4" \
144
+       tsdecrypt ... \
145
+         --emm \
146
+         --emm-filter reject_all \
147
+         --emm-filter accept/name=Bulcrypt_EMMs,offset=0,data=82 \
148
+         --emm-filter accept/name=Bulcrypt_EMMs,data=84 \
149
+         --emm-filter accept/name=Bulcrypt_EMMs,data=85 \

+ 11
- 4
data.h View File

201
 	FILTER_REJECT     = 4,
201
 	FILTER_REJECT     = 4,
202
 };
202
 };
203
 
203
 
204
+enum filter_type {
205
+	FILTER_TYPE_DATA = 0,				// Compare data at offset X
206
+	FILTER_TYPE_MASK = 1,				// Compare data + mask
207
+};
208
+
204
 struct filter {
209
 struct filter {
205
-	enum filter_action action;			// If set to 1, the filter is negative else the filter is positive
206
-	uint8_t		offset;					// Offset into EMM
207
-	uint8_t		data_len;				// Filter length
208
-	uint8_t		data[MAX_FILTER_LEN];	// Filter bytes
210
+	enum filter_action action;
211
+	enum filter_type type;
212
+	uint8_t		offset;					// Offset into EMM, if offset == 255 then mask exists, ignore the offset
213
+	uint8_t		filter_len;				// Filter length
214
+	uint8_t		data[MAX_FILTER_LEN];	// Data | Matched bytes
215
+	uint8_t		mask[MAX_FILTER_LEN];	// Mask bytes
209
 	char		name[MAX_FILTER_NAME];	// Filter name (default: NO_NAME)
216
 	char		name[MAX_FILTER_NAME];	// Filter name (default: NO_NAME)
210
 };
217
 };
211
 
218
 

+ 143
- 53
filter.c View File

18
 #include "data.h"
18
 #include "data.h"
19
 #include "filter.h"
19
 #include "filter.h"
20
 
20
 
21
+struct filter_actions_map {
22
+	const char			*token;
23
+	bool				last;
24
+	enum filter_action	type;
25
+};
26
+
27
+static struct filter_actions_map action_tokens[] = {
28
+	{ "accept_all", true,  FILTER_ACCEPT_ALL },
29
+	{ "acceptall",  true,  FILTER_ACCEPT_ALL },
30
+	{ "reject_all", true,  FILTER_REJECT_ALL },
31
+	{ "rejectall",  true,  FILTER_REJECT_ALL },
32
+	{ "accept",     false, FILTER_ACCEPT     },
33
+	{ "reject",     false, FILTER_REJECT     },
34
+	{ NULL,         true,  FILTER_NO_MATCH   },
35
+};
36
+
37
+enum filter_token { T_UNKNOWN, T_NAME, T_OFFSET, T_DATA, T_MATCH, T_MASK };
38
+
39
+struct filter_data_map {
40
+	const char			*token;
41
+	enum filter_token	type;
42
+};
43
+
44
+static struct filter_data_map data_tokens[] = {
45
+	{ "name",    T_NAME    },
46
+	{ "ofs",     T_OFFSET  },
47
+	{ "offset",  T_OFFSET  },
48
+	{ "data",    T_DATA    },
49
+	{ "match",   T_MATCH   },
50
+	{ "mask",    T_MASK    },
51
+	{ NULL,      T_UNKNOWN },
52
+};
53
+
54
+int parse_hex(char *data, uint8_t *output, uint8_t *output_len, uint8_t output_size) {
55
+	int i, len = strlen(data), consumed = 0;
56
+	uint8_t local_output_len = 0;
57
+	if (!output_len)
58
+		output_len = &local_output_len;
59
+	for (i = 0; i < len; i++) { // Parse data (01 02 03 04 ...)
60
+		char ch = toupper(data[i]);
61
+		// Skip 0x prefixes
62
+		if (i + 1 < len && ch == '0' && toupper(data[i + 1]) == 'X')
63
+			continue;
64
+		if (!isxdigit(ch))
65
+			continue;
66
+		ch -= ch > 64 ? 55 : 48; // hex2dec
67
+		if (consumed % 2 == 0) {
68
+			output[*output_len] = 0; // Reset
69
+			output[*output_len] += ch << 4;
70
+		} else {
71
+			output[*output_len] += ch;
72
+			(*output_len)++;
73
+			if (*output_len + 1 >= output_size) {
74
+				fprintf(stderr, "WARN : Too much filter data (max %d bytes), ignoring last bytes: %s\n",
75
+					output_size, data + i + 2);
76
+				break;
77
+			}
78
+		}
79
+		consumed++;
80
+	}
81
+	return *output_len;
82
+}
83
+
21
 int filter_parse(char *filter_def, struct filter *filter) {
84
 int filter_parse(char *filter_def, struct filter *filter) {
22
-	int i, j, k, ret = 0;
85
+	int j, k, ret = 0;
23
 	char *str1, *saveptr1;
86
 	char *str1, *saveptr1;
24
 	char *f = strdup(filter_def);
87
 	char *f = strdup(filter_def);
25
 	memset(filter, 0, sizeof(struct filter));
88
 	memset(filter, 0, sizeof(struct filter));
89
+	memset(filter->mask, 0xff, sizeof(filter->mask));
26
 	snprintf(filter->name, sizeof(filter->name), "NONAME");
90
 	snprintf(filter->name, sizeof(filter->name), "NONAME");
27
 	for (j = 1, str1 = f; ; j++, str1 = NULL) {
91
 	for (j = 1, str1 = f; ; j++, str1 = NULL) {
28
 		char *token = strtok_r(str1, "/", &saveptr1);
92
 		char *token = strtok_r(str1, "/", &saveptr1);
29
 		if (token == NULL)
93
 		if (token == NULL)
30
 			break;
94
 			break;
31
 		if (j == 1) { // First token, the command
95
 		if (j == 1) { // First token, the command
32
-			if (strstr(token, "accept_all") == token || strstr(token, "acceptall") == token) {
33
-				filter->action = FILTER_ACCEPT_ALL;
34
-				ret = 1;
35
-				goto OUT; // Other tokens are not needed
36
-			} else if (strstr(token, "reject_all") == token || strstr(token, "rejectall") == token) {
37
-				filter->action = FILTER_REJECT_ALL;
38
-				ret = 1;
39
-				goto OUT; // Other tokens are not needed
40
-			} else if (strstr(token, "accept") == token) {
41
-				filter->action = FILTER_ACCEPT;
42
-				continue; // Continue looking for other tokes
43
-			} else if (strstr(token, "reject") == token) {
44
-				filter->action = FILTER_REJECT;
45
-				continue; // Continue looking for other tokes
46
-			} else {
96
+			struct filter_actions_map *m;
97
+			for (m = action_tokens; m->token; m++) {
98
+				if (strstr(token, m->token) == token) {
99
+					filter->action = m->type;
100
+					ret = 1;
101
+					if (m->last)
102
+						goto OUT; // Other tokens are not needed
103
+					break;
104
+				}
105
+			}
106
+			if (filter->action == FILTER_NO_MATCH) {
47
 				fprintf(stderr, "ERROR: Unknown filter command: %s\n", token);
107
 				fprintf(stderr, "ERROR: Unknown filter command: %s\n", token);
48
 				ret = 0;
108
 				ret = 0;
49
 				goto OUT; // Other tokens are not needed
109
 				goto OUT; // Other tokens are not needed
55
 				char *token2 = strtok_r(str2, ",", &saveptr2);
115
 				char *token2 = strtok_r(str2, ",", &saveptr2);
56
 				if (token2 == NULL)
116
 				if (token2 == NULL)
57
 					break;
117
 					break;
58
-				char *eq = strrchr(token2, '=');
59
-				if (eq) {
60
-					if (strstr(token2, "ofs") == token2 || strstr(token2, "offset") == token2) {
61
-						filter->offset = strtoul(eq + 1, NULL, 0);
62
-					} else if (strstr(token2, "name") == token2) {
63
-						snprintf(filter->name, sizeof(filter->name), "%s", eq + 1);
64
-					} else if (strstr(token2, "data") == token2) {
65
-						char *data = eq + 1;
66
-						int len = strlen(data), consumed = 0;
67
-						for (i = 0; i < len; i++) { // Parse data (01 02 03 04 ...)
68
-							char ch = toupper(data[i]);
69
-							// Skip 0x prefixes
70
-							if (i + 1 < len && ch == '0' && toupper(data[i + 1]) == 'X')
71
-								continue;
72
-							if (!isxdigit(ch))
73
-								continue;
74
-							ch -= ch > 64 ? 55 : 48; // hex2dec
75
-							if (consumed % 2 == 0) {
76
-								filter->data[filter->data_len  ] += ch << 4;
77
-							} else {
78
-								filter->data[filter->data_len++] += ch;
79
-								if (filter->data_len + 1 >= MAX_FILTER_LEN) {
80
-									fprintf(stderr, "WARN : Too much filter data (max %d bytes), ignoring last bytes: %s\n",
81
-										MAX_FILTER_LEN, data + i + 2);
82
-									break;
83
-								}
84
-							}
85
-							consumed++;
118
+				char *tokdata = strrchr(token2, '=');
119
+				if (tokdata) {
120
+					tokdata++; // Skip =
121
+					struct filter_data_map *m;
122
+					enum filter_token data_type = T_UNKNOWN;
123
+					for (m = data_tokens; m->token; m++) {
124
+						if (strstr(token2, m->token) == token2) {
125
+							data_type = m->type;
126
+							break;
86
 						}
127
 						}
87
-						ret = filter->data_len;
88
-					} else {
128
+					}
129
+					switch (data_type) {
130
+					case T_NAME:
131
+						snprintf(filter->name, sizeof(filter->name), "%s", tokdata);
132
+						break;
133
+					case T_OFFSET:
134
+						filter->offset = strtoul(tokdata, NULL, 0);
135
+						break;
136
+					case T_DATA:
137
+						ret = parse_hex(tokdata, filter->data, &filter->filter_len, MAX_FILTER_LEN);
138
+						break;
139
+					case T_MATCH:
140
+						filter->type = FILTER_TYPE_MASK;
141
+						ret = parse_hex(tokdata, filter->data, &filter->filter_len, MAX_FILTER_LEN);
142
+						break;
143
+					case T_MASK:
144
+						filter->type = FILTER_TYPE_MASK;
145
+						ret = parse_hex(tokdata, filter->mask, NULL, MAX_FILTER_LEN);
146
+						break;
147
+					default:
89
 						fprintf(stderr, "WARN : Unknown filter setting: %s\n", token2);
148
 						fprintf(stderr, "WARN : Unknown filter setting: %s\n", token2);
90
 					}
149
 					}
91
 				}
150
 				}
108
 	if (filter->action == FILTER_ACCEPT || filter->action == FILTER_REJECT)
167
 	if (filter->action == FILTER_ACCEPT || filter->action == FILTER_REJECT)
109
 		pos += snprintf(buffer + pos, buf_len - pos, " Name: %-20s", filter->name);
168
 		pos += snprintf(buffer + pos, buf_len - pos, " Name: %-20s", filter->name);
110
 	if (filter->action == FILTER_ACCEPT || filter->action == FILTER_REJECT) {
169
 	if (filter->action == FILTER_ACCEPT || filter->action == FILTER_REJECT) {
111
-		char tmp[MAX_FILTER_LEN * 6];
112
-		ts_hex_dump_buf(tmp, sizeof(tmp), filter->data, filter->data_len, 0);
113
-		pos += snprintf(buffer + pos, buf_len - pos, " Offset: %2d Data: %s", filter->offset, tmp);
170
+		char tmp_data[MAX_FILTER_LEN * 6], tmp_mask[MAX_FILTER_LEN * 6];
171
+		ts_hex_dump_buf(tmp_data, sizeof(tmp_data), filter->data, filter->filter_len, 0);
172
+		switch (filter->type) {
173
+		case FILTER_TYPE_DATA:
174
+			pos += snprintf(buffer + pos, buf_len - pos, " Offset: %2d Data: %s", filter->offset, tmp_data);
175
+			break;
176
+		case FILTER_TYPE_MASK:
177
+			ts_hex_dump_buf(tmp_mask, sizeof(tmp_mask), filter->mask, filter->filter_len, 0);
178
+			pos += snprintf(buffer + pos, buf_len - pos, " Match: %s Mask: %s", tmp_data, tmp_mask);
179
+			break;
180
+		} // switch (filter->type)
114
 	}
181
 	}
115
 }
182
 }
116
 
183
 
118
 	if (filter->action == FILTER_ACCEPT_ALL || filter->action == FILTER_REJECT_ALL)
185
 	if (filter->action == FILTER_ACCEPT_ALL || filter->action == FILTER_REJECT_ALL)
119
 		return filter->action;
186
 		return filter->action;
120
 	if (filter->action == FILTER_ACCEPT || filter->action == FILTER_REJECT) {
187
 	if (filter->action == FILTER_ACCEPT || filter->action == FILTER_REJECT) {
121
-		if (filter->data_len + filter->offset > data_len)
122
-			return FILTER_NO_MATCH;
123
-		if (memcmp(data + filter->offset, filter->data, filter->data_len) == 0)
124
-			return filter->action;
188
+		switch (filter->type) {
189
+		case FILTER_TYPE_DATA: {
190
+			if (filter->filter_len + filter->offset > data_len)
191
+				return FILTER_NO_MATCH;
192
+			if (memcmp(data + filter->offset, filter->data, filter->filter_len) == 0)
193
+				return filter->action;
194
+			break;
195
+		}
196
+		case FILTER_TYPE_MASK: {
197
+			if ((unsigned int)filter->filter_len + 3 > data_len)
198
+				return FILTER_NO_MATCH;
199
+			int matched = 0;
200
+			// Check data[0] against filter->data[0]
201
+			if ((data[0] & filter->mask[0]) == (filter->data[0] & filter->mask[0])) {
202
+				matched++;
203
+				int i;
204
+				for (i = 1; i < filter->filter_len; i++) {
205
+					// Check data[3...] against filter->data[1...]
206
+					if ((data[i + 2] & filter->mask[i]) == (filter->data[i] & filter->mask[i]))
207
+						matched++;
208
+				}
209
+			}
210
+			if (matched == filter->filter_len)
211
+				return filter->action;
212
+			break;
213
+		}
214
+		} // switch (filter->type)
125
 	}
215
 	}
126
 	return FILTER_NO_MATCH;
216
 	return FILTER_NO_MATCH;
127
 }
217
 }

Loading…
Cancel
Save